E57 is the ASTM standard E2807, which is sold rather than published freely. This page follows the open-source reference implementation, libE57Format (descended from the libE57 library written alongside the standard), and describes version 1.0. All binary numbers are little-endian except the page checksums.
File header
48 bytes at the start of the file.
| Offset | Size | Type | Name | Meaning |
|---|---|---|---|---|
| 0 | 8 | char[8] | fileSignature | ASTM-E57 |
| 8 | 4 | uint32 | majorVersion | 1 |
| 12 | 4 | uint32 | minorVersion | 0 |
| 16 | 8 | uint64 | filePhysicalLength | Size of the file in bytes, a multiple of the page size |
| 24 | 8 | uint64 | xmlPhysicalOffset | Physical offset of the XML section |
| 32 | 8 | uint64 | xmlLogicalLength | Length of the XML in bytes, not counting checksums |
| 40 | 8 | uint64 | pageSize | 1024 |
Pages and checksums
The whole file, header included, is a sequence of 1,024-byte pages. Each page holds 1,020 bytes of data followed by a 4-byte CRC-32C checksum of those 1,020 bytes (polynomial 0x1EDC6F41, reflected, initial value and final XOR 0xFFFFFFFF), stored with its most significant byte first.
Offsets therefore come in two kinds. A physical offset counts every byte of the file. A logical offset counts only data bytes, skipping the checksums. To convert: page = logical ÷ 1020, physical = page × 1024 + logical mod 1020. Offsets stored in the file (in the header, sections and XML) are physical; lengths are logical.
Binary sections
Binary data lives in sections, each starting with a section header whose first byte gives its type. The XML refers to a section by its physical offset.
Blob section (ID 0)
| Offset | Size | Type | Name | Meaning |
|---|---|---|---|---|
| 0 | 1 | uint8 | sectionId | 0 |
| 1 | 7 | uint8[7] | reserved | 0 |
| 8 | 8 | uint64 | sectionLogicalLength | Length of the whole section, header included |
The blob’s bytes follow the 16-byte header; a Blob element in the XML
gives their length. Blobs hold whole files such as JPEG and PNG images.
Compressed vector section (ID 1)
| Offset | Size | Type | Name | Meaning |
|---|---|---|---|---|
| 0 | 1 | uint8 | sectionId | 1 |
| 1 | 7 | uint8[7] | reserved | 0 |
| 8 | 8 | uint64 | sectionLogicalLength | Length of the whole section |
| 16 | 8 | uint64 | dataPhysicalOffset | Physical offset of the first data packet |
| 24 | 8 | uint64 | indexPhysicalOffset | Physical offset of the first index packet, or 0 |
Packets
A compressed vector section is a series of packets, each at most 64 KiB long and a multiple of 4 bytes. The first byte gives the packet type.
| Type | Packet |
|---|---|
| 0 | Index packet |
| 1 | Data packet |
| 2 | Empty packet (padding) |
Data packet
| Offset | Size | Type | Name | Meaning |
|---|---|---|---|---|
| 0 | 1 | uint8 | packetType | 1 |
| 1 | 1 | uint8 | packetFlags | Flag bits; normally 0 |
| 2 | 2 | uint16 | packetLogicalLengthMinus1 | Packet length − 1 |
| 4 | 2 | uint16 | bytestreamCount | Number of bytestreams, one per field of the record |
| 6 | 2 × count | uint16[] | buffer lengths | Bytes of each bytestream in this packet |
| … | … | bytes | bytestream buffers | The buffers one after another, then padding to a multiple of 4 |
Each field of the records (such as cartesianX) has its own bytestream, in
the order of the fields in the vector’s prototype. A field’s values continue from one
packet to the next.
Index packet
| Offset | Size | Type | Name | Meaning |
|---|---|---|---|---|
| 0 | 1 | uint8 | packetType | 0 |
| 1 | 1 | uint8 | packetFlags | 0 |
| 2 | 2 | uint16 | packetLogicalLengthMinus1 | Packet length − 1 |
| 4 | 2 | uint16 | entryCount | Entries, at most 2,048 |
| 6 | 1 | uint8 | indexLevel | 0 for entries pointing at data packets; higher levels point at index packets |
| 7 | 9 | uint8[9] | reserved | 0 |
| 16 | 16 × count | entries | entries | Each a uint64 record number and the uint64 physical offset of the packet where that record starts |
Index packets are optional; they let a reader jump to a record without reading the packets before it.
Empty packet
Four bytes: packet type 2, a reserved 0 byte and a uint16 length − 1, followed by unused bytes up to that length.
Bytestream encoding
Each element type in a record is packed into its bytestream in its own way:
| Type | Stored as |
|---|---|
Integer, ScaledInteger | value − minimum, in the fewest bits that can hold maximum − minimum (⌈log₂(maximum − minimum + 1)⌉), packed one after another from the lowest bit. A field whose minimum equals its maximum takes no bits at all |
Float single | 4-byte IEEE floats |
Float double | 8-byte IEEE floats |
String | Each string has a length prefix, then UTF-8 bytes. If the lowest bit of the first prefix byte is 0, the prefix is that one byte and the length is the byte shifted right by 1 (up to 127). If it is 1, the prefix is 8 bytes, a little-endian number whose bits above the lowest give the length |
A ScaledInteger’s value is then raw × scale + offset. For example a
cartesianX with minimum −100000, maximum 100000 and scale 0.001 needs
⌈log₂ 200001⌉ = 18 bits per point. Stored bits worth 100500 give the integer
100500 + (−100000) = 500, which is 500 × 0.001 = 0.5.