From e35aa9719131a3948cc4a31b084b4357468cb283 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 30 Nov 2024 18:46:25 +0100 Subject: [PATCH] Added 'read_bytes' without options and 'write_bytes'. Added tests for write_bytes and for Ruby. Doc fixed. --- src/db/db/gsiDeclDbLayout.cc | 24 ++++++++++++++++++++++ src/db/db/gsiDeclDbReader.cc | 26 +++++++++++++++++++++--- testdata/python/dbLayoutTest.py | 15 ++++++++++++++ testdata/ruby/dbLayoutTests2.rb | 36 +++++++++++++++++++++++++++++++++ 4 files changed, 98 insertions(+), 3 deletions(-) diff --git a/src/db/db/gsiDeclDbLayout.cc b/src/db/db/gsiDeclDbLayout.cc index 8ae853f04..b4eed69dc 100644 --- a/src/db/db/gsiDeclDbLayout.cc +++ b/src/db/db/gsiDeclDbLayout.cc @@ -713,6 +713,20 @@ write_options2 (db::Layout *layout, const std::string &filename, bool /*gzip*/, write_options1 (layout, filename, options); } +static std::vector +write_bytes (db::Layout *layout, const db::SaveLayoutOptions &options) +{ + tl::OutputMemoryStream byte_stream; + + { + db::Writer writer (options); + tl::OutputStream stream (byte_stream); + writer.write (*layout, stream); + } + + return std::vector (byte_stream.data (), byte_stream.data () + byte_stream.size ()); +} + static void check_layer (const db::Layout *layout, unsigned int layer) { if (! layout->is_valid_layer (layer) && ! layout->is_special_layer (layer)) { @@ -2399,6 +2413,16 @@ Class decl_Layout ("db", "Layout", "@brief Writes the layout to a stream file\n" "@param filename The file to which to write the layout\n" ) + + gsi::method_ext ("write_bytes", &write_bytes, gsi::arg ("options"), + "@brief Writes the layout to a binary string\n" + "@param options The option set to use for writing. See \\SaveLayoutOptions for details. Options are used specifically to define the format to use.\n" + "\n" + "Instead of writing a file, this function generates a binary string. As there is no filename, the " + "format cannot be determined from the suffix. It needs to be specified in the options. A " + "function that reads bytes is \\read_bytes.\n" + "\n" + "This method has been introduced in version 0.29.9.\n" + ) + gsi::method_ext ("clip", &clip, gsi::arg ("cell"), gsi::arg ("box"), "@brief Clips the given cell by the given rectangle and produce a new cell with the clip\n" "@param cell The cell index of the cell to clip\n" diff --git a/src/db/db/gsiDeclDbReader.cc b/src/db/db/gsiDeclDbReader.cc index 981e19b15..33d3f5ecd 100644 --- a/src/db/db/gsiDeclDbReader.cc +++ b/src/db/db/gsiDeclDbReader.cc @@ -433,10 +433,19 @@ namespace gsi return reader.read (*layout, options); } + static db::LayerMap + load_bytes (db::Layout *layout, const std::vector &bytes) + { + tl::InputMemoryStream byte_stream (bytes.data (), bytes.size ()); + tl::InputStream stream (byte_stream); + db::Reader reader (stream); + return reader.read (*layout); + } + static db::LayerMap load_bytes_with_options (db::Layout *layout, const std::vector &bytes, const db::LoadLayoutOptions &options) { - tl::InputMemoryStream byte_stream (bytes.data(), bytes.size()); + tl::InputMemoryStream byte_stream (bytes.data (), bytes.size ()); tl::InputStream stream (byte_stream); db::Reader reader (stream); return reader.read (*layout, options); @@ -464,15 +473,26 @@ namespace gsi "\n" "This method has been added in version 0.18." ) + + gsi::method_ext ("read_bytes", &load_bytes, gsi::arg ("bytes"), + "@brief Load the layout from the given bytes array\n" + "The format of the file is determined automatically and automatic unzipping is provided. " + "A function that creates a byte string is \\write_bytes.\n" + "\n" + "@param bytes The data to load.\n" + "@return A layer map that contains the mapping used by the reader including the layers that have been created." + "\n" + "This method has been added in version 0.29.9." + ) + gsi::method_ext ("read_bytes", &load_bytes_with_options, gsi::arg ("bytes"), gsi::arg ("options"), "@brief Load the layout from the given bytes array with options\n" "The format of the file is determined automatically and automatic unzipping is provided. " - "In this version, some reader options can be specified. " + "In this version, some reader options can be specified. A function that creates a byte string is \\write_bytes.\n" + "\n" "@param bytes The data to load.\n" "@param options The options object specifying further options for the reader.\n" "@return A layer map that contains the mapping used by the reader including the layers that have been created." "\n" - "This method has been added in version 0.29." + "This method has been added in version 0.29.9." ), "" ); diff --git a/testdata/python/dbLayoutTest.py b/testdata/python/dbLayoutTest.py index 8f88d52d9..c5710cfce 100644 --- a/testdata/python/dbLayoutTest.py +++ b/testdata/python/dbLayoutTest.py @@ -1220,6 +1220,21 @@ class DBLayoutTest(unittest.TestCase): l2 = ly2.layer(1, 0) self.assertEqual(ly2.top_cell().bbox().to_s(), "(0,10;20,30)") + def test_write_bytes(self): + + ly = pya.Layout() + top = ly.create_cell("TOP") + l1 = ly.layer(1, 0) + shape = top.shapes(l1).insert(pya.Box(0, 10, 20, 30)) + options = pya.SaveLayoutOptions() + options.format = "GDS2" + byte_buffer = ly.write_bytes(options) + + ly2 = pya.Layout() + ly2.read_bytes(byte_buffer) + l2 = ly2.layer(1, 0) + self.assertEqual(ly2.top_cell().bbox().to_s(), "(0,10;20,30)") + # run unit tests if __name__ == '__main__': diff --git a/testdata/ruby/dbLayoutTests2.rb b/testdata/ruby/dbLayoutTests2.rb index 3985738d9..7fd6cb3b8 100644 --- a/testdata/ruby/dbLayoutTests2.rb +++ b/testdata/ruby/dbLayoutTests2.rb @@ -1445,6 +1445,42 @@ class DBLayoutTests2_TestClass < TestBase end + def test_read_bytes + + file_gds = File::join($ut_testtmp, "bytes.gds") + + ly = RBA::Layout::new + top = ly.create_cell("TOP") + l1 = ly.layer(1, 0) + shape = top.shapes(l1).insert(RBA::Box::new(0, 10, 20, 30)) + ly.write(file_gds) + + byte_buffer = File.open(file_gds, "rb") { |f| f.read } + + ly2 = RBA::Layout::new + ly2.read_bytes(byte_buffer, RBA::LoadLayoutOptions::new) + l2 = ly2.layer(1, 0) + assert_equal(ly2.top_cell.bbox.to_s, "(0,10;20,30)") + + end + + def test_write_bytes + + ly = RBA::Layout::new + top = ly.create_cell("TOP") + l1 = ly.layer(1, 0) + shape = top.shapes(l1).insert(RBA::Box::new(0, 10, 20, 30)) + options = RBA::SaveLayoutOptions::new + options.format = "GDS2" + byte_buffer = ly.write_bytes(options) + + ly2 = RBA::Layout::new + ly2.read_bytes(byte_buffer) + l2 = ly2.layer(1, 0) + assert_equal(ly2.top_cell.bbox.to_s, "(0,10;20,30)") + + end + end load("test_epilogue.rb")