From ba98ff5b34e9f7e97d7dc7c431892d2248f3938e Mon Sep 17 00:00:00 2001 From: Emily Boudreaux Date: Sun, 16 Feb 2025 12:14:41 -0500 Subject: [PATCH 1/3] test(opatIO/tests): updated test file to be written by utils/opatio --- tests/opatIO/opatIOTest.cpp | 8 ++++---- tests/opatIO/{example.opat => test.opat} | Bin 417696 -> 417696 bytes 2 files changed, 4 insertions(+), 4 deletions(-) rename tests/opatIO/{example.opat => test.opat} (99%) mode change 100755 => 100644 diff --git a/tests/opatIO/opatIOTest.cpp b/tests/opatIO/opatIOTest.cpp index deec76b..9b51e27 100644 --- a/tests/opatIO/opatIOTest.cpp +++ b/tests/opatIO/opatIOTest.cpp @@ -6,7 +6,7 @@ #include #include -std::string EXAMPLE_FILENAME = std::string(getenv("MESON_SOURCE_ROOT")) + "/tests/opatIO/example.opat"; +std::string EXAMPLE_FILENAME = std::string(getenv("MESON_SOURCE_ROOT")) + "/tests/opatIO/test.opat"; /** * @file opatIOTest.cpp @@ -39,9 +39,9 @@ TEST_F(opatIOTest, Header) { EXPECT_EQ(header.numTables, 20); EXPECT_EQ(header.headerSize, 256); EXPECT_EQ(header.indexOffset, 416416); - EXPECT_EQ(std::string(header.creationDate), "Feb 14, 2025"); - EXPECT_EQ(std::string(header.sourceInfo), "MESA 12700, Synthetic Opacity Data"); - EXPECT_EQ(std::string(header.comment), "log10 kappa (cm^2/g)"); + EXPECT_EQ(std::string(header.creationDate), "Feb 16, 2025"); + EXPECT_EQ(std::string(header.sourceInfo), "no source provided by user"); + EXPECT_EQ(std::string(header.comment), "default header"); } TEST_F(opatIOTest, TableIndex) { diff --git a/tests/opatIO/example.opat b/tests/opatIO/test.opat old mode 100755 new mode 100644 similarity index 99% rename from tests/opatIO/example.opat rename to tests/opatIO/test.opat index 9d5384ffc51148c26a5ad6791997d15ba87bb27e..f321effffd0da07292d8f3fa816cb50185963745 GIT binary patch delta 117 zcmZ3mUvj~INeBM`#}Gya5e6V&WMEhj#Rg(AxTPj37@FxQ7#SFug7|s)3dQ-QMaiiO z1x5K~nJK9$3Q3g;rNya56C2g|Qc}|rOLIyTGEx&$KqB=MC-62gwlM+`QyU|58zV~_ LBkMLswgqtj`v@Rx delta 133 zcmZ3mUvj~INiY8Z#}Gya1_luzW&ko4M6rQb3~s4O3Wg>+3PuJ-rXaqrYp|n&p^>?P zfsR6OWnM`}YDs3Yf`36`a%M@Tf=gma;zUO|k(~T=Lj#5E#DaoE1&!p~I3xXZ&54cm c69ZVA7~2?uh^dW{xs8#fjgfU5Bin*F05d8fr~m)} From 5345b4c80186fb43714ea981a94788ad13660824 Mon Sep 17 00:00:00 2001 From: Emily Boudreaux Date: Sun, 16 Feb 2025 12:15:11 -0500 Subject: [PATCH 2/3] fix(opatIO): fixed header packing bug --- src/opatIO/private/opatIO.cpp | 27 +++++++++++++++++++++++++++ src/opatIO/public/opatIO.h | 2 ++ 2 files changed, 29 insertions(+) diff --git a/src/opatIO/private/opatIO.cpp b/src/opatIO/private/opatIO.cpp index 7a8adb2..b637d38 100644 --- a/src/opatIO/private/opatIO.cpp +++ b/src/opatIO/private/opatIO.cpp @@ -11,6 +11,26 @@ #include #include +// Function to check system endianness +bool is_big_endian() { + uint16_t test = 0x1; + return reinterpret_cast(&test)[0] == 0; +} + +// Generic function to swap bytes for any type +template +T swap_bytes(T value) { + static_assert(std::is_trivially_copyable::value, "swap_bytes only supports trivial types."); + T result; + uint8_t* src = reinterpret_cast(&value); + uint8_t* dest = reinterpret_cast(&result); + for (size_t i = 0; i < sizeof(T); i++) { + dest[i] = src[sizeof(T) - 1 - i]; + } + return result; +} + + // Constructor OpatIO::OpatIO() {} @@ -57,6 +77,12 @@ void OpatIO::readHeader(std::ifstream &file) { if (file.gcount() != sizeof(Header)) { throw std::runtime_error("Error reading header from file: " + filename); } + + if (is_big_endian()) { + header.version = swap_bytes(header.version); + header.numTables = swap_bytes(header.numTables); + header.indexOffset = swap_bytes(header.indexOffset); + } } // Read the table index from the file @@ -67,6 +93,7 @@ void OpatIO::readTableIndex(std::ifstream &file) { if (file.gcount() != static_cast(header.numTables * sizeof(TableIndex))) { throw std::runtime_error("Error reading table index from file: " + filename); } + buildTableIDToComposition(); } diff --git a/src/opatIO/public/opatIO.h b/src/opatIO/public/opatIO.h index 859615f..1da6ee9 100644 --- a/src/opatIO/public/opatIO.h +++ b/src/opatIO/public/opatIO.h @@ -9,6 +9,7 @@ #include #include +#pragma pack(1) struct Header { char magic[4]; uint16_t version; @@ -21,6 +22,7 @@ struct Header { char reserved[26]; }; +#pragma pack() struct TableIndex { double X; double Z; From 3b34ce3979540cbd8b073b99ac3c2206205ea1c6 Mon Sep 17 00:00:00 2001 From: Emily Boudreaux Date: Sun, 16 Feb 2025 12:16:20 -0500 Subject: [PATCH 3/3] feat(utils/opatio): added print to ascii --- utils/opatio/src/opatio/opat/opat.py | 89 ++++++++++++++++++++++++++-- 1 file changed, 85 insertions(+), 4 deletions(-) diff --git a/utils/opatio/src/opatio/opat/opat.py b/utils/opatio/src/opatio/opat/opat.py index ef091f7..81abd61 100644 --- a/utils/opatio/src/opatio/opat/opat.py +++ b/utils/opatio/src/opatio/opat/opat.py @@ -186,6 +186,87 @@ class OpatIO: return tableIndexBytes + def __repr__(self) -> str: + reprString = f"""OpatIO( + versoion: {self.header.version} + numTables: {self.header.numTables} + headerSize: {self.header.headerSize} + indexOffset: {self.header.indexOffset} + creationDate: {self.header.creationDate} + sourceInfo: {self.header.sourceInfo} + comment: {self.header.comment} + reserved: {self.header.reserved} +)""" + return reprString + + def _format_table_as_string(self, table: OPATTable, X: float, Z: float) -> str: + tableString: List[str] = [] + # fixed width X and Z header per table + tableString.append(f"X: {X:<10.4f} Z: {Z:<10.4f}") + tableString.append("-" * 80) + # write logR accross the top (reserving one col for where logT will be) + logRRow = f"{"":<10}" + logRRowTrue = "".join(f"{r:<10.4f}" for r in table.logR) + tableString.append(logRRow + logRRowTrue) + for i, logT in enumerate(table.logT): + row = f"{logT:<10.4f}" + for kappa in table.logKappa[i]: + row += f"{kappa:<10.4f}" + tableString.append(row) + tableString.append("=" * 80) + return '\n'.join(tableString) + + @staticmethod + def print_table_indexes(table_indexes: List[TableIndex]) -> str: + if not table_indexes: + print("No table indexes found.") + return + + tableRows: List[str] = [] + tableRows.append("\nTable Indexes in OPAT File:\n") + tableRows.append(f"{'X':<10} {'Z':<10} {'Byte Start':<15} {'Byte End':<15} {'Checksum (SHA-256)'}") + tableRows.append("=" * 80) + for entry in table_indexes: + tableRows.append(f"{entry.X:<10.4f} {entry.Z:<10.4f} {entry.byteStart:<15} {entry.byteEnd:<15} {entry.sha256[:16]}...") + return '\n'.join(tableRows) + + def save_as_ascii(self, filename: str) -> str: + numericFMT = "{:.18e}" + currentStartByte: int = 256 + tableIndexs: List[bytes] = [] + tableStrings: List[bytes] = [] + for (X, Z), table in self.tables: + checksum, tableBytes = self._table_bytes(table) + tableStrings.append(self._format_table_as_string(table, X, Z) + "\n") + tableIndex = TableIndex( + X = X, + Z = Z, + byteStart = currentStartByte, + byteEnd = currentStartByte + len(tableBytes), + sha256 = checksum + ) + tableIndexs.append(tableIndex) + + + currentStartByte += len(tableBytes) + self.header.indexOffset = currentStartByte + with open(filename, 'w') as f: + f.write(f"{self.header.magic}\n") + f.write(f"Version: {self.header.version}\n") + f.write(f"numTables: {self.header.numTables}\n") + f.write(f"headerSize (bytes): {self.header.headerSize}\n") + f.write(f"tableIndex Offset (bytes): {self.header.indexOffset}\n") + f.write(f"Creation Date: {self.header.creationDate}\n") + f.write(f"Source Info: {self.header.sourceInfo}\n") + f.write(f"Comment: {self.header.comment}\n") + f.write("="*80 + "\n") + f.write("="*80 + "\n") + for tableString in tableStrings: + f.write(tableString) + f.write("="*80 + "\n") + f.write("="*80 + "\n") + f.write(self.print_table_indexes(tableIndexs)) + def save(self, filename: str) -> str: tempHeaderBytes = self._header_bytes() @@ -229,14 +310,14 @@ def loadOpat(filename: str) -> OpatIO: headerBytes: bytes = f.read(256) unpackedHeader = struct.unpack("<4s H I I Q 16s 64s 128s 26s", headerBytes) loadedHeader = Header( - magic = unpackedHeader[0].decode(), + magic = unpackedHeader[0].decode().replace("\x00", ""), version = unpackedHeader[1], numTables = unpackedHeader[2], headerSize = unpackedHeader[3], indexOffset = unpackedHeader[4], - creationDate = unpackedHeader[5].decode(), - sourceInfo = unpackedHeader[6].decode(), - comment = unpackedHeader[7].decode(), + creationDate = unpackedHeader[5].decode().replace("\x00", ""), + sourceInfo = unpackedHeader[6].decode().replace("\x00", ""), + comment = unpackedHeader[7].decode().replace("\x00", ""), reserved = unpackedHeader[8] ) opat.header = loadedHeader