Last active
April 18, 2026 23:41
-
-
Save etscrivner/bf3f1476b72365e6d482fd84d1cf65ab to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| oh4_lbp_serializer.h - v0.1 - public domain | |
| Authored 2026 by Eric Scrivner | |
| no warranty implied; use at your own risk | |
| Before including, | |
| #define OH4_LBP_SERIALIZER_IMPLEMENTATION | |
| in the file that you want to have the implementation. | |
| ABOUT: | |
| Provides simple, versioned, little-endian binary serialization in the | |
| style of Media Molecule / Little Big Planet. Multi-byte values are read | |
| and written as native little-endian host data; big-endian hosts are not | |
| supported. | |
| See: https://handmade.network/p/29/swedish-cubes-for-unity/blog/p/2723-how_media_molecule_does_serialization | |
| WORKFLOW: | |
| Define one serialize function per type. The same function handles both | |
| reading and writing. Fields are gated by a single monolithic | |
| DataVersion. Proper versioning requires the caller to define and validate a | |
| file header that carries at least a data version. On reads, deserialize and | |
| validate that header first, then assign `serializer.DataVersion` from the | |
| validated file version before deserializing versioned payload fields. | |
| typedef struct MyFileHeader MyFileHeader; | |
| struct MyFileHeader { | |
| LBPS_U32 Magic; | |
| LBPS_U32 Version; | |
| }; | |
| static void MyFileHeaderSerialize(LBPS_Serializer* s, MyFileHeader* header) { | |
| LBPS_SerializeU32(s, &header->Magic); | |
| LBPS_SerializeU32(s, &header->Version); | |
| } | |
| #define kMyFileMagic 0xDEADBEEF | |
| #define kCurrentVerison 12 | |
| MyFileHeader header = { kMyFileMagic, kCurrentVersion }; | |
| LBPS_Serializer s = LBPS_Measure(kCurrentVersion); | |
| MyFileHeaderSerialize(&s, &header); | |
| MyTypeSerialize(&s, &value); | |
| required = LBPS_BytesRequired(&s); | |
| LBPS_Serializer w = LBPS_Write(buffer, required, kCurrentVersion); | |
| MyFileHeaderSerialize(&w, &header); | |
| MyTypeSerialize(&w, &value); | |
| MyFileHeader file_header = { 0 }; | |
| LBPS_Serializer r = LBPS_Read(buffer, used, kCurrentVersion); | |
| MyFileHeaderSerialize(&r, &file_header); | |
| ASSERT( file_header.Magic == kMyFileMagic ); | |
| ASSERT( file_header.Version >= 1 && file_header.Version <= kCurrentVersion ); | |
| r.DataVersion = file_header.Version; | |
| MyTypeSerialize(&r, &value); | |
| For bounded chunks and backpatched container headers, assume the parent | |
| serializer has already consumed and validated any enclosing file header | |
| and has had `DataVersion` assigned: | |
| LBPS_PatchSite size_site = LBPS_ReserveU32(&w); | |
| LBPS_SIZE chunk_begin = LBPS_BytesUsed(&w); | |
| WriteChunkPayload(&w); | |
| LBPS_PatchU32(&w, size_site, (LBPS_U32)(LBPS_BytesUsed(&w) - chunk_begin)); | |
| LBPS_Serializer chunk = LBPS_Chunk(&r, chunk_size); | |
| ReadChunkPayload(&chunk); | |
| LBPS_Finish(&chunk, kLBPS_Finish_Exact); | |
| MODES: | |
| Read consumes bytes from a caller-provided buffer | |
| Write writes bytes into a caller-provided buffer | |
| Measure computes the required byte count without touching memory | |
| I/O: | |
| By default, LBPS works on caller-provided memory buffers. For larger files | |
| or custom storage backends, LBPS also supports stream serializers through | |
| user-provided absolute-offset callbacks: | |
| LBPS_IOReadAtFn | |
| LBPS_IOWriteAtFn | |
| LBPS_IOFlushFn | |
| LBPS_StreamRead(...) | |
| LBPS_StreamWrite(...) | |
| LBPS_Flush(...) | |
| The stream API is intentionally generic and does not depend on stdio or any | |
| platform-specific file layer. Callers provide the read/write/flush callbacks | |
| that make sense for their runtime. Stream callbacks operate on absolute byte | |
| offsets and may be backed by files, mapped memory, archive subranges, or any | |
| other random-access storage. Stream reads follow the same caller-defined | |
| header flow as buffer reads: read and validate your file header first, then | |
| assign `DataVersion` before deserializing versioned payload fields. | |
| FIELD MACROS: | |
| LBPS_ADD(s, datum, version_added, TypeTag, FieldName) | |
| LBPS_ADD_BYTES(s, datum, version_added, FieldName, ByteCount) | |
| LBPS_REM(s, datum, version_added, version_removed, TypeTag, FieldName, DefaultValue) | |
| `LBPS_ADD` serializes a field that exists in the current runtime struct. | |
| `LBPS_ADD_BYTES` serializes a field as raw bytes with an explicit byte count. | |
| `LBPS_REM` materializes a temporary local for an older field that no | |
| longer exists in the runtime struct, allowing upgrade logic after the | |
| field list has been visited. | |
| CONTAINER HELPERS: | |
| LBPS_BytesRemaining(s) | |
| LBPS_Finish(s, FinishMode) | |
| LBPS_Chunk(parent, Size) | |
| `LBPS_Chunk` creates a bounded child serializer over `Size` bytes while | |
| advancing the parent stream by that same amount. This is useful for | |
| size-delimited chunks and optional trailing sections. In Measure mode, | |
| bounded chunks are validated when you call `LBPS_Finish` on the child. | |
| BACKPATCH HELPERS: | |
| LBPS_Reserve(s, Size) | |
| LBPS_ReserveU32(s) | |
| LBPS_PatchBytes(s, Site, Data, Size) | |
| LBPS_PatchU32(s, Site, Value) | |
| Reserve helpers advance the stream and hand back a patch site that can be | |
| filled later once the final size, offset, or checksum is known. | |
| TYPE TAGS: | |
| Built-in tags: | |
| U8 S8 U16 S16 U32 S32 F32 | |
| U64 S64 F64 (unless LBPS_NO_64_BIT is defined) | |
| Custom tags can be added by defining: | |
| #define LBPS_TYPE_MyTag MyType | |
| #define LBPS_SERIALIZE_MyTag MyTypeSerialize | |
| OVERRIDES: | |
| #define LBPS_TYPES_ALREADY_DEFINED | |
| Provide your own LBPS_U8/LBPS_S32/etc typedefs. | |
| #define LBPS_NO_64_BIT | |
| Remove 64-bit integer and F64 helpers. | |
| #define LBPS_COPY_BYTES(dst, src, size) | |
| Override byte copy. If not provided, memcpy is used by default. Define | |
| LBPS_NO_CRT to force the internal byte-copy fallback instead. | |
| EXAMPLE: | |
| This payload example assumes any enclosing file header has already been | |
| read, validated, and used to assign `DataVersion` on read serializers. If | |
| you wrap this payload in a file header, include those header bytes in your | |
| measure/write passes too. | |
| enum { | |
| SV_Initial = 1, | |
| SV_Fouls, | |
| SV_NoFouls, | |
| }; | |
| typedef struct ScoreState ScoreState; | |
| struct ScoreState { | |
| LBPS_S32 P1Score; | |
| LBPS_S32 P2Score; | |
| }; | |
| static void ScoreStateSerialize(LBPS_Serializer* s, ScoreState* datum) { | |
| LBPS_ADD(s, datum, SV_Initial, S32, P1Score); | |
| LBPS_ADD(s, datum, SV_Initial, S32, P2Score); | |
| LBPS_REM(s, datum, SV_Fouls, SV_NoFouls, S32, P1Fouls, 0); | |
| LBPS_REM(s, datum, SV_Fouls, SV_NoFouls, S32, P2Fouls, 0); | |
| if ( s->Mode == kLBPS_Mode_Read && s->DataVersion < SV_NoFouls ) { | |
| datum->P1Score -= P1Fouls; | |
| datum->P2Score -= P2Fouls; | |
| } | |
| } | |
| LBPS_Serializer m = LBPS_Measure(SV_NoFouls); | |
| ScoreStateSerialize(&m, &state); | |
| LBPS_SIZE needed = LBPS_BytesRequired(&m); | |
| LBPS_Serializer w = LBPS_Write(buffer, needed, SV_NoFouls); | |
| ScoreStateSerialize(&w, &state); | |
| typedef struct MyStream MyStream; | |
| LBPS_SIZE MyReadAt(void* user, LBPS_SIZE offset, void* dst, LBPS_SIZE size); | |
| LBPS_SIZE MyWriteAt(void* user, LBPS_SIZE offset, void* src, LBPS_SIZE size); | |
| LBPS_IO io = { | |
| .User = stream, | |
| .ReadAt = MyReadAt, | |
| .WriteAt = MyWriteAt, | |
| }; | |
| LBPS_Serializer sw = LBPS_StreamWrite(&io, SV_NoFouls); | |
| ScoreStateSerialize(&sw, &state); | |
| LBPS_Flush(&sw); | |
| */ | |
| #ifndef OH4_LBP_SERIALIZER_H | |
| #define OH4_LBP_SERIALIZER_H | |
| #ifndef LBPS_DEF | |
| #ifdef OH4_LBPS_STATIC | |
| #define LBPS_DEF static | |
| #else | |
| #ifdef __cplusplus | |
| #define LBPS_DEF extern "C" | |
| #else | |
| #define LBPS_DEF extern | |
| #endif | |
| #endif | |
| #endif | |
| /* === Configuration === */ | |
| #ifndef LBPS_TYPES_ALREADY_DEFINED | |
| #include <stdint.h> | |
| typedef uint8_t LBPS_U8; | |
| typedef int8_t LBPS_S8; | |
| typedef uint16_t LBPS_U16; | |
| typedef int16_t LBPS_S16; | |
| typedef uint32_t LBPS_U32; | |
| typedef int32_t LBPS_S32; | |
| typedef float LBPS_F32; | |
| #ifndef LBPS_NO_64_BIT | |
| typedef uint64_t LBPS_U64; | |
| typedef int64_t LBPS_S64; | |
| typedef double LBPS_F64; | |
| #endif | |
| #endif | |
| #if !defined(LBPS_COPY_BYTES) && !defined(LBPS_NO_CRT) | |
| #include <string.h> | |
| #endif | |
| #ifndef LBPS_SIZE_TYPE_DEFINED | |
| #ifndef LBPS_NO_64_BIT | |
| typedef LBPS_U64 LBPS_SIZE; | |
| #else | |
| typedef LBPS_U32 LBPS_SIZE; | |
| #endif | |
| #endif | |
| /* === Enums === */ | |
| typedef enum eLBPS_Mode { | |
| kLBPS_Mode_Read, | |
| kLBPS_Mode_Write, | |
| kLBPS_Mode_Measure, | |
| kLBPS_Mode_COUNT | |
| } eLBPS_Mode; | |
| typedef enum eLBPS_Error { | |
| kLBPS_Error_None, | |
| kLBPS_Error_EndOfBuffer, | |
| kLBPS_Error_BadSize, | |
| kLBPS_Error_BadData, | |
| kLBPS_Error_OutOfSpace, | |
| kLBPS_Error_COUNT | |
| } eLBPS_Error; | |
| typedef enum eLBPS_FinishMode { | |
| kLBPS_Finish_Exact, | |
| kLBPS_Finish_AllowTrailing, | |
| kLBPS_FinishMode_COUNT | |
| } eLBPS_FinishMode; | |
| /* === Structs === */ | |
| /* Absolute-offset stream callbacks for generic random-access storage. */ | |
| typedef LBPS_SIZE LBPS_IOReadAtFn(void* user, LBPS_SIZE offset, void* dst, LBPS_SIZE size); | |
| typedef LBPS_SIZE LBPS_IOWriteAtFn(void* user, LBPS_SIZE offset, void* src, LBPS_SIZE size); | |
| typedef LBPS_S32 LBPS_IOFlushFn(void* user); | |
| typedef struct LBPS_IO LBPS_IO; | |
| struct LBPS_IO { | |
| void* User; | |
| LBPS_IOReadAtFn* ReadAt; | |
| LBPS_IOWriteAtFn* WriteAt; | |
| LBPS_IOFlushFn* Flush; | |
| }; | |
| typedef struct LBPS_Serializer LBPS_Serializer; | |
| struct LBPS_Serializer { | |
| LBPS_S32 IsWriting; /* 1 = writing or measuring, 0 = reading */ | |
| eLBPS_Mode Mode; | |
| eLBPS_Error Error; | |
| LBPS_U32 DataVersion; | |
| LBPS_U32 LatestVersion; | |
| LBPS_SIZE Offset; | |
| LBPS_SIZE Required; | |
| LBPS_S32 IsBounded; | |
| LBPS_SIZE Limit; | |
| LBPS_SIZE BaseOffset; | |
| LBPS_S32 IsStream; | |
| LBPS_IO* IO; | |
| LBPS_U8* Start; | |
| LBPS_U8* Ptr; | |
| LBPS_U8* End; | |
| }; | |
| typedef struct LBPS_PatchSite LBPS_PatchSite; | |
| struct LBPS_PatchSite { | |
| LBPS_SIZE Offset; | |
| LBPS_SIZE Size; | |
| }; | |
| /* === Functions === */ | |
| /* Constructors */ | |
| LBPS_DEF LBPS_Serializer LBPS_Read(void* data, LBPS_SIZE size, LBPS_U32 latest_version); | |
| LBPS_DEF LBPS_Serializer LBPS_Write(void* data, LBPS_SIZE size, LBPS_U32 version); | |
| LBPS_DEF LBPS_Serializer LBPS_Measure(LBPS_U32 version); | |
| /* Unbuffered stream serializers over user-provided absolute-offset callbacks. */ | |
| LBPS_DEF LBPS_Serializer LBPS_StreamRead(LBPS_IO* io, LBPS_SIZE size, LBPS_U32 latest_version); | |
| LBPS_DEF LBPS_Serializer LBPS_StreamWrite(LBPS_IO* io, LBPS_U32 version); | |
| /* State helpers */ | |
| LBPS_DEF LBPS_S32 LBPS_IsValid(LBPS_Serializer* s); | |
| LBPS_DEF void LBPS_SetError(LBPS_Serializer* s, eLBPS_Error error); | |
| LBPS_DEF LBPS_SIZE LBPS_BytesUsed(LBPS_Serializer* s); | |
| LBPS_DEF LBPS_SIZE LBPS_BytesRequired(LBPS_Serializer* s); | |
| LBPS_DEF LBPS_SIZE LBPS_BytesRemaining(LBPS_Serializer* s); | |
| LBPS_DEF LBPS_S32 LBPS_Finish(LBPS_Serializer* s, eLBPS_FinishMode mode); | |
| LBPS_DEF LBPS_S32 LBPS_Flush(LBPS_Serializer* s); | |
| LBPS_DEF LBPS_Serializer LBPS_Chunk(LBPS_Serializer* parent, LBPS_SIZE size); | |
| /* Backpatch helpers */ | |
| LBPS_DEF LBPS_PatchSite LBPS_Reserve(LBPS_Serializer* s, LBPS_SIZE size); | |
| LBPS_DEF LBPS_PatchSite LBPS_ReserveU32(LBPS_Serializer* s); | |
| LBPS_DEF LBPS_S32 LBPS_PatchBytes(LBPS_Serializer* s, LBPS_PatchSite site, void* data, LBPS_SIZE size); | |
| LBPS_DEF LBPS_S32 LBPS_PatchU32(LBPS_Serializer* s, LBPS_PatchSite site, LBPS_U32 value); | |
| /* Primitive serialization */ | |
| LBPS_DEF void LBPS_SerializeU8(LBPS_Serializer* s, LBPS_U8* value); | |
| LBPS_DEF void LBPS_SerializeS8(LBPS_Serializer* s, LBPS_S8* value); | |
| LBPS_DEF void LBPS_SerializeU16(LBPS_Serializer* s, LBPS_U16* value); | |
| LBPS_DEF void LBPS_SerializeS16(LBPS_Serializer* s, LBPS_S16* value); | |
| LBPS_DEF void LBPS_SerializeU32(LBPS_Serializer* s, LBPS_U32* value); | |
| LBPS_DEF void LBPS_SerializeS32(LBPS_Serializer* s, LBPS_S32* value); | |
| LBPS_DEF void LBPS_SerializeF32(LBPS_Serializer* s, LBPS_F32* value); | |
| LBPS_DEF void LBPS_SerializeBytes(LBPS_Serializer* s, void* data, LBPS_SIZE size); | |
| LBPS_DEF void LBPS_Skip(LBPS_Serializer* s, LBPS_SIZE size); | |
| LBPS_DEF void LBPS_Align(LBPS_Serializer* s, LBPS_SIZE alignment, LBPS_U8 pad_byte); | |
| #ifndef LBPS_NO_64_BIT | |
| LBPS_DEF void LBPS_SerializeU64(LBPS_Serializer* s, LBPS_U64* value); | |
| LBPS_DEF void LBPS_SerializeS64(LBPS_Serializer* s, LBPS_S64* value); | |
| LBPS_DEF void LBPS_SerializeF64(LBPS_Serializer* s, LBPS_F64* value); | |
| #endif | |
| /* | |
| === Typed Field Helpers === | |
| Typed field helpers for LBP-style field lists. | |
| Built-in tags are provided for primitives. Custom tags can be added by | |
| defining both `LBPS_TYPE_<Tag>` and `LBPS_SERIALIZE_<Tag>`. | |
| */ | |
| #define LBPS_TYPE_U8 LBPS_U8 | |
| #define LBPS_TYPE_S8 LBPS_S8 | |
| #define LBPS_TYPE_U16 LBPS_U16 | |
| #define LBPS_TYPE_S16 LBPS_S16 | |
| #define LBPS_TYPE_U32 LBPS_U32 | |
| #define LBPS_TYPE_S32 LBPS_S32 | |
| #define LBPS_TYPE_F32 LBPS_F32 | |
| #ifndef LBPS_NO_64_BIT | |
| #define LBPS_TYPE_U64 LBPS_U64 | |
| #define LBPS_TYPE_S64 LBPS_S64 | |
| #define LBPS_TYPE_F64 LBPS_F64 | |
| #endif | |
| #define LBPS_SERIALIZE_U8 LBPS_SerializeU8 | |
| #define LBPS_SERIALIZE_S8 LBPS_SerializeS8 | |
| #define LBPS_SERIALIZE_U16 LBPS_SerializeU16 | |
| #define LBPS_SERIALIZE_S16 LBPS_SerializeS16 | |
| #define LBPS_SERIALIZE_U32 LBPS_SerializeU32 | |
| #define LBPS_SERIALIZE_S32 LBPS_SerializeS32 | |
| #define LBPS_SERIALIZE_F32 LBPS_SerializeF32 | |
| #ifndef LBPS_NO_64_BIT | |
| #define LBPS_SERIALIZE_U64 LBPS_SerializeU64 | |
| #define LBPS_SERIALIZE_S64 LBPS_SerializeS64 | |
| #define LBPS_SERIALIZE_F64 LBPS_SerializeF64 | |
| #endif | |
| #define LBPS_ADD(_s, _datum, _field_added_version, _type_tag, _field_name) \ | |
| if ( (_s)->DataVersion >= (_field_added_version) ) { \ | |
| LBPS_SERIALIZE_##_type_tag((_s), &((_datum)->_field_name)); \ | |
| } | |
| #define LBPS_ADD_BYTES(_s, _datum, _field_added_version, _field_name, _byte_count) \ | |
| if ( (_s)->DataVersion >= (_field_added_version) ) { \ | |
| LBPS_SerializeBytes((_s), (_datum)->_field_name, (LBPS_SIZE)(_byte_count)); \ | |
| } | |
| #define LBPS_REM(_s, _datum, _field_added_version, _field_removed_version, _type_tag, _field_name, _default_value) \ | |
| LBPS_TYPE_##_type_tag _field_name = (_default_value); \ | |
| if ( (_s)->DataVersion >= (_field_added_version) && \ | |
| (_s)->DataVersion < (_field_removed_version) ) { \ | |
| LBPS_SERIALIZE_##_type_tag((_s), &(_field_name)); \ | |
| } | |
| #if defined(OH4_LBP_SERIALIZER_IMPLEMENTATION) | |
| #if !defined(LBPS_COPY_BYTES) | |
| #if defined(LBPS_NO_CRT) | |
| static void LBPS__FallbackCopyBytes(LBPS_U8* dst, LBPS_U8* src, LBPS_SIZE size) { | |
| while ( size > 0 ) { | |
| *dst++ = *src++; | |
| size -= 1; | |
| } | |
| } | |
| #define LBPS_COPY_BYTES(dst, src, size) LBPS__FallbackCopyBytes((LBPS_U8*)(dst), (LBPS_U8*)(src), (size)) | |
| #else | |
| #define LBPS_COPY_BYTES(dst, src, size) memcpy((dst), (src), (size)) | |
| #endif | |
| #endif | |
| static LBPS_S32 LBPS__AdvanceChecked(LBPS_Serializer* s, LBPS_SIZE size) { | |
| LBPS_SIZE max_value = (LBPS_SIZE)~(LBPS_SIZE)0; | |
| if ( s->Offset > max_value - size ) { | |
| LBPS_SetError(s, kLBPS_Error_BadSize); | |
| return( 0 ); | |
| } | |
| return( 1 ); | |
| } | |
| static void LBPS__Advance(LBPS_Serializer* s, LBPS_SIZE size) { | |
| s->Offset += size; | |
| if ( s->Mode != kLBPS_Mode_Read ) { | |
| s->Required = s->Offset; | |
| } | |
| if ( s->Mode != kLBPS_Mode_Measure && !s->IsStream ) { | |
| s->Ptr += size; | |
| } | |
| } | |
| static LBPS_S32 LBPS__AbsoluteOffset(LBPS_Serializer* s, LBPS_SIZE relative, LBPS_SIZE* out_offset) { | |
| if ( out_offset == 0 ) { | |
| LBPS_SetError(s, kLBPS_Error_BadData); | |
| return( 0 ); | |
| } | |
| if ( s->BaseOffset > (LBPS_SIZE)~(LBPS_SIZE)0 - relative ) { | |
| LBPS_SetError(s, kLBPS_Error_BadSize); | |
| return( 0 ); | |
| } | |
| *out_offset = s->BaseOffset + relative; | |
| return( 1 ); | |
| } | |
| static LBPS_S32 LBPS__StreamWriteFill(LBPS_Serializer* s, LBPS_SIZE size, LBPS_U8 value) { | |
| LBPS_SIZE abs_off = 0; | |
| LBPS_SIZE relative = 0; | |
| LBPS_SIZE remaining = 0; | |
| LBPS_U8 temp[64]; | |
| LBPS_SIZE fill_size = 0; | |
| if ( s->IO == 0 || s->IO->WriteAt == 0 ) { | |
| LBPS_SetError(s, kLBPS_Error_BadData); | |
| return( 0 ); | |
| } | |
| if ( size == 0 ) { | |
| return( 1 ); | |
| } | |
| while ( fill_size < (LBPS_SIZE)sizeof(temp) ) { | |
| temp[fill_size] = value; | |
| fill_size += 1; | |
| } | |
| relative = s->Offset; | |
| remaining = size; | |
| while ( remaining > 0 ) { | |
| LBPS_SIZE chunk_size = remaining; | |
| if ( chunk_size > (LBPS_SIZE)sizeof(temp) ) { | |
| chunk_size = (LBPS_SIZE)sizeof(temp); | |
| } | |
| if ( !LBPS__AbsoluteOffset(s, relative, &abs_off) ) { | |
| return( 0 ); | |
| } | |
| if ( s->IO->WriteAt(s->IO->User, abs_off, temp, chunk_size) != chunk_size ) { | |
| LBPS_SetError(s, kLBPS_Error_OutOfSpace); | |
| return( 0 ); | |
| } | |
| relative += chunk_size; | |
| remaining -= chunk_size; | |
| } | |
| return( 1 ); | |
| } | |
| static LBPS_S32 LBPS__StreamReadExact(LBPS_Serializer* s, LBPS_SIZE relative, void* dst, LBPS_SIZE size) { | |
| LBPS_SIZE abs_off = 0; | |
| LBPS_SIZE read_size = 0; | |
| if ( s->IO == 0 || s->IO->ReadAt == 0 || ( dst == 0 && size != 0 ) ) { | |
| LBPS_SetError(s, kLBPS_Error_BadData); | |
| return( 0 ); | |
| } | |
| if ( !LBPS__AbsoluteOffset(s, relative, &abs_off) ) { | |
| return( 0 ); | |
| } | |
| read_size = s->IO->ReadAt(s->IO->User, abs_off, dst, size); | |
| if ( read_size != size ) { | |
| LBPS_SetError(s, kLBPS_Error_EndOfBuffer); | |
| return( 0 ); | |
| } | |
| return( 1 ); | |
| } | |
| static LBPS_S32 LBPS__StreamWriteExact(LBPS_Serializer* s, LBPS_SIZE relative, void* src, LBPS_SIZE size) { | |
| LBPS_SIZE abs_off = 0; | |
| LBPS_SIZE write_size = 0; | |
| if ( s->IO == 0 || s->IO->WriteAt == 0 || ( src == 0 && size != 0 ) ) { | |
| LBPS_SetError(s, kLBPS_Error_BadData); | |
| return( 0 ); | |
| } | |
| if ( !LBPS__AbsoluteOffset(s, relative, &abs_off) ) { | |
| return( 0 ); | |
| } | |
| write_size = s->IO->WriteAt(s->IO->User, abs_off, src, size); | |
| if ( write_size != size ) { | |
| LBPS_SetError(s, kLBPS_Error_OutOfSpace); | |
| return( 0 ); | |
| } | |
| return( 1 ); | |
| } | |
| static LBPS_S32 LBPS__CanRead(LBPS_Serializer* s, LBPS_SIZE size) { | |
| LBPS_SIZE remaining = 0; | |
| if ( !LBPS__AdvanceChecked(s, size) ) { | |
| return( 0 ); | |
| } | |
| if ( size == 0 ) { | |
| return( 1 ); | |
| } | |
| if ( s->IsStream ) { | |
| if ( s->IO == 0 || s->IO->ReadAt == 0 ) { | |
| LBPS_SetError(s, kLBPS_Error_BadData); | |
| return( 0 ); | |
| } | |
| if ( s->IsBounded ) { | |
| remaining = LBPS_BytesRemaining(s); | |
| if ( remaining < size ) { | |
| LBPS_SetError(s, kLBPS_Error_EndOfBuffer); | |
| return( 0 ); | |
| } | |
| } | |
| return( 1 ); | |
| } | |
| if ( s->Ptr == 0 || s->End == 0 ) { | |
| LBPS_SetError(s, kLBPS_Error_EndOfBuffer); | |
| return( 0 ); | |
| } | |
| remaining = (LBPS_SIZE)(s->End - s->Ptr); | |
| if ( remaining < size ) { | |
| LBPS_SetError(s, kLBPS_Error_EndOfBuffer); | |
| return( 0 ); | |
| } | |
| return( 1 ); | |
| } | |
| static LBPS_S32 LBPS__CanWrite(LBPS_Serializer* s, LBPS_SIZE size) { | |
| LBPS_SIZE remaining = 0; | |
| if ( !LBPS__AdvanceChecked(s, size) ) { | |
| return( 0 ); | |
| } | |
| if ( size == 0 ) { | |
| return( 1 ); | |
| } | |
| if ( s->IsStream ) { | |
| if ( s->IO == 0 || s->IO->WriteAt == 0 ) { | |
| LBPS_SetError(s, kLBPS_Error_BadData); | |
| return( 0 ); | |
| } | |
| if ( s->IsBounded ) { | |
| remaining = LBPS_BytesRemaining(s); | |
| if ( remaining < size ) { | |
| LBPS_SetError(s, kLBPS_Error_OutOfSpace); | |
| return( 0 ); | |
| } | |
| } | |
| return( 1 ); | |
| } | |
| if ( s->Ptr == 0 || s->End == 0 ) { | |
| LBPS_SetError(s, kLBPS_Error_OutOfSpace); | |
| return( 0 ); | |
| } | |
| remaining = (LBPS_SIZE)(s->End - s->Ptr); | |
| if ( remaining < size ) { | |
| LBPS_SetError(s, kLBPS_Error_OutOfSpace); | |
| return( 0 ); | |
| } | |
| return( 1 ); | |
| } | |
| static LBPS_S32 LBPS__PatchSiteInBounds(LBPS_Serializer* s, LBPS_PatchSite site, LBPS_SIZE size) { | |
| LBPS_SIZE end = 0; | |
| LBPS_SIZE limit = 0; | |
| if ( size != site.Size ) { | |
| LBPS_SetError(s, kLBPS_Error_BadSize); | |
| return( 0 ); | |
| } | |
| if ( site.Offset > (LBPS_SIZE)~(LBPS_SIZE)0 - size ) { | |
| LBPS_SetError(s, kLBPS_Error_BadSize); | |
| return( 0 ); | |
| } | |
| end = site.Offset + size; | |
| limit = s->Required; | |
| if ( end > limit ) { | |
| LBPS_SetError(s, kLBPS_Error_BadSize); | |
| return( 0 ); | |
| } | |
| return( 1 ); | |
| } | |
| LBPS_DEF LBPS_Serializer LBPS_Read(void* data, LBPS_SIZE size, LBPS_U32 latest_version) { | |
| LBPS_Serializer result = { 0 }; | |
| LBPS_U8* start = (LBPS_U8*)data; | |
| result.IsWriting = 0; | |
| result.Mode = kLBPS_Mode_Read; | |
| result.Error = ( start == 0 && size != 0 ) ? kLBPS_Error_BadData : kLBPS_Error_None; | |
| result.DataVersion = 0; | |
| result.LatestVersion = latest_version; | |
| result.Offset = 0; | |
| result.Required = 0; | |
| result.IsBounded = 1; | |
| result.Limit = size; | |
| result.BaseOffset = 0; | |
| result.IsStream = 0; | |
| result.IO = 0; | |
| result.Start = start; | |
| result.Ptr = start; | |
| result.End = (start != 0) ? (start + size) : 0; | |
| return( result ); | |
| } | |
| LBPS_DEF LBPS_Serializer LBPS_Write(void* data, LBPS_SIZE size, LBPS_U32 version) { | |
| LBPS_Serializer result = { 0 }; | |
| LBPS_U8* start = (LBPS_U8*)data; | |
| result.IsWriting = 1; | |
| result.Mode = kLBPS_Mode_Write; | |
| result.Error = ( start == 0 && size != 0 ) ? kLBPS_Error_BadData : kLBPS_Error_None; | |
| result.DataVersion = version; | |
| result.LatestVersion = version; | |
| result.Offset = 0; | |
| result.Required = 0; | |
| result.IsBounded = 1; | |
| result.Limit = size; | |
| result.BaseOffset = 0; | |
| result.IsStream = 0; | |
| result.IO = 0; | |
| result.Start = start; | |
| result.Ptr = start; | |
| result.End = (start != 0) ? (start + size) : 0; | |
| return( result ); | |
| } | |
| LBPS_DEF LBPS_Serializer LBPS_Measure(LBPS_U32 version) { | |
| LBPS_Serializer result = { 0 }; | |
| result.IsWriting = 1; | |
| result.Mode = kLBPS_Mode_Measure; | |
| result.Error = kLBPS_Error_None; | |
| result.DataVersion = version; | |
| result.LatestVersion = version; | |
| result.Offset = 0; | |
| result.Required = 0; | |
| result.IsBounded = 0; | |
| result.Limit = 0; | |
| result.BaseOffset = 0; | |
| result.IsStream = 0; | |
| result.IO = 0; | |
| result.Start = 0; | |
| result.Ptr = 0; | |
| result.End = 0; | |
| return( result ); | |
| } | |
| LBPS_DEF LBPS_Serializer LBPS_StreamRead(LBPS_IO* io, LBPS_SIZE size, LBPS_U32 latest_version) { | |
| LBPS_Serializer result = { 0 }; | |
| result.IsWriting = 0; | |
| result.Mode = kLBPS_Mode_Read; | |
| result.Error = ( io == 0 || io->ReadAt == 0 ) ? kLBPS_Error_BadData : kLBPS_Error_None; | |
| result.DataVersion = 0; | |
| result.LatestVersion = latest_version; | |
| result.Offset = 0; | |
| result.Required = 0; | |
| result.IsBounded = 1; | |
| result.Limit = size; | |
| result.BaseOffset = 0; | |
| result.IsStream = 1; | |
| result.IO = io; | |
| result.Start = 0; | |
| result.Ptr = 0; | |
| result.End = 0; | |
| return( result ); | |
| } | |
| LBPS_DEF LBPS_Serializer LBPS_StreamWrite(LBPS_IO* io, LBPS_U32 version) { | |
| LBPS_Serializer result = { 0 }; | |
| result.IsWriting = 1; | |
| result.Mode = kLBPS_Mode_Write; | |
| result.Error = ( io == 0 || io->WriteAt == 0 ) ? kLBPS_Error_BadData : kLBPS_Error_None; | |
| result.DataVersion = version; | |
| result.LatestVersion = version; | |
| result.Offset = 0; | |
| result.Required = 0; | |
| result.IsBounded = 0; | |
| result.Limit = 0; | |
| result.BaseOffset = 0; | |
| result.IsStream = 1; | |
| result.IO = io; | |
| result.Start = 0; | |
| result.Ptr = 0; | |
| result.End = 0; | |
| return( result ); | |
| } | |
| LBPS_DEF LBPS_S32 LBPS_IsValid(LBPS_Serializer* s) { | |
| return( s->Error == kLBPS_Error_None ); | |
| } | |
| LBPS_DEF void LBPS_SetError(LBPS_Serializer* s, eLBPS_Error error) { | |
| if ( s->Error == kLBPS_Error_None ) { | |
| s->Error = error; | |
| } | |
| } | |
| LBPS_DEF LBPS_SIZE LBPS_BytesUsed(LBPS_Serializer* s) { | |
| return( s->Offset ); | |
| } | |
| LBPS_DEF LBPS_SIZE LBPS_BytesRequired(LBPS_Serializer* s) { | |
| return( s->Required ); | |
| } | |
| LBPS_DEF LBPS_SIZE LBPS_BytesRemaining(LBPS_Serializer* s) { | |
| LBPS_SIZE result = 0; | |
| if ( s != 0 && s->IsBounded ) { | |
| if ( s->Offset < s->Limit ) { | |
| result = s->Limit - s->Offset; | |
| } | |
| } | |
| return( result ); | |
| } | |
| LBPS_DEF LBPS_S32 LBPS_Finish(LBPS_Serializer* s, eLBPS_FinishMode mode) { | |
| LBPS_S32 result = 0; | |
| if ( s == 0 ) { | |
| return( 0 ); | |
| } | |
| if ( s->Error != kLBPS_Error_None ) { | |
| return( 0 ); | |
| } | |
| if ( s->IsBounded ) { | |
| if ( mode == kLBPS_Finish_Exact ) { | |
| if ( s->Offset != s->Limit ) { | |
| LBPS_SetError(s, kLBPS_Error_BadSize); | |
| } | |
| } | |
| else if ( mode == kLBPS_Finish_AllowTrailing ) { | |
| if ( s->Offset > s->Limit ) { | |
| LBPS_SetError(s, kLBPS_Error_BadSize); | |
| } | |
| } | |
| else { | |
| LBPS_SetError(s, kLBPS_Error_BadData); | |
| } | |
| } | |
| else if ( mode != kLBPS_Finish_Exact && mode != kLBPS_Finish_AllowTrailing ) { | |
| LBPS_SetError(s, kLBPS_Error_BadData); | |
| } | |
| result = ( s->Error == kLBPS_Error_None ); | |
| return( result ); | |
| } | |
| LBPS_DEF LBPS_S32 LBPS_Flush(LBPS_Serializer* s) { | |
| LBPS_S32 result = 0; | |
| if ( s == 0 ) { | |
| return( 0 ); | |
| } | |
| if ( s->Error != kLBPS_Error_None ) { | |
| return( 0 ); | |
| } | |
| result = 1; | |
| if ( s->IsStream && s->IO != 0 && s->IO->Flush != 0 ) { | |
| result = s->IO->Flush(s->IO->User); | |
| if ( !result ) { | |
| LBPS_SetError(s, kLBPS_Error_BadData); | |
| } | |
| } | |
| return( result && s->Error == kLBPS_Error_None ); | |
| } | |
| LBPS_DEF LBPS_Serializer LBPS_Chunk(LBPS_Serializer* parent, LBPS_SIZE size) { | |
| LBPS_Serializer result = { 0 }; | |
| if ( parent == 0 ) { | |
| result.Error = kLBPS_Error_BadData; | |
| return( result ); | |
| } | |
| result.IsWriting = parent->IsWriting; | |
| result.Mode = parent->Mode; | |
| result.Error = parent->Error; | |
| result.DataVersion = parent->DataVersion; | |
| result.LatestVersion = parent->LatestVersion; | |
| result.Offset = 0; | |
| result.Required = 0; | |
| result.IsBounded = 1; | |
| result.Limit = size; | |
| result.BaseOffset = 0; | |
| result.IsStream = parent->IsStream; | |
| result.IO = parent->IO; | |
| result.Start = parent->Ptr; | |
| result.Ptr = parent->Ptr; | |
| result.End = ( parent->Ptr != 0 ) ? ( parent->Ptr + size ) : 0; | |
| if ( parent->Error != kLBPS_Error_None ) { | |
| return( result ); | |
| } | |
| if ( !LBPS__AbsoluteOffset(parent, parent->Offset, &result.BaseOffset) ) { | |
| result.Error = parent->Error; | |
| result.Start = 0; | |
| result.Ptr = 0; | |
| result.End = 0; | |
| return( result ); | |
| } | |
| if ( parent->Mode == kLBPS_Mode_Read ) { | |
| if ( !LBPS__CanRead(parent, size) ) { | |
| result.Error = parent->Error; | |
| result.Start = 0; | |
| result.Ptr = 0; | |
| result.End = 0; | |
| return( result ); | |
| } | |
| } | |
| else if ( parent->Mode == kLBPS_Mode_Write ) { | |
| if ( !LBPS__CanWrite(parent, size) ) { | |
| result.Error = parent->Error; | |
| result.Start = 0; | |
| result.Ptr = 0; | |
| result.End = 0; | |
| return( result ); | |
| } | |
| } | |
| else { | |
| if ( !LBPS__AdvanceChecked(parent, size) ) { | |
| result.Error = parent->Error; | |
| result.Start = 0; | |
| result.Ptr = 0; | |
| result.End = 0; | |
| return( result ); | |
| } | |
| result.Start = 0; | |
| result.Ptr = 0; | |
| result.End = 0; | |
| } | |
| if ( parent->IsStream ) { | |
| result.Start = 0; | |
| result.Ptr = 0; | |
| result.End = 0; | |
| } | |
| LBPS__Advance(parent, size); | |
| return( result ); | |
| } | |
| LBPS_DEF LBPS_PatchSite LBPS_Reserve(LBPS_Serializer* s, LBPS_SIZE size) { | |
| LBPS_PatchSite result = { 0 }; | |
| if ( s == 0 ) { | |
| return( result ); | |
| } | |
| if ( s->Mode == kLBPS_Mode_Read ) { | |
| LBPS_SetError(s, kLBPS_Error_BadData); | |
| return( result ); | |
| } | |
| result.Offset = s->Offset; | |
| result.Size = size; | |
| LBPS_Skip(s, size); | |
| return( result ); | |
| } | |
| LBPS_DEF LBPS_PatchSite LBPS_ReserveU32(LBPS_Serializer* s) { | |
| return( LBPS_Reserve(s, 4) ); | |
| } | |
| LBPS_DEF LBPS_S32 LBPS_PatchBytes(LBPS_Serializer* s, LBPS_PatchSite site, void* data, LBPS_SIZE size) { | |
| LBPS_S32 result = 0; | |
| LBPS_SIZE abs_off = 0; | |
| if ( s == 0 ) { | |
| return( 0 ); | |
| } | |
| if ( s->Error != kLBPS_Error_None ) { | |
| return( 0 ); | |
| } | |
| if ( s->Mode == kLBPS_Mode_Read ) { | |
| LBPS_SetError(s, kLBPS_Error_BadData); | |
| return( 0 ); | |
| } | |
| if ( size != 0 && data == 0 ) { | |
| LBPS_SetError(s, kLBPS_Error_BadData); | |
| return( 0 ); | |
| } | |
| if ( !LBPS__PatchSiteInBounds(s, site, size) ) { | |
| return( 0 ); | |
| } | |
| if ( s->Mode == kLBPS_Mode_Write && size != 0 ) { | |
| if ( s->IsStream ) { | |
| if ( !LBPS__AbsoluteOffset(s, site.Offset, &abs_off) ) { | |
| return( 0 ); | |
| } | |
| if ( s->IO == 0 || s->IO->WriteAt == 0 ) { | |
| LBPS_SetError(s, kLBPS_Error_BadData); | |
| return( 0 ); | |
| } | |
| if ( s->IO->WriteAt(s->IO->User, abs_off, data, size) != size ) { | |
| LBPS_SetError(s, kLBPS_Error_OutOfSpace); | |
| return( 0 ); | |
| } | |
| } | |
| else { | |
| if ( s->Start == 0 ) { | |
| LBPS_SetError(s, kLBPS_Error_BadData); | |
| return( 0 ); | |
| } | |
| LBPS_COPY_BYTES(s->Start + site.Offset, (void*)data, size); | |
| } | |
| } | |
| result = 1; | |
| return( result ); | |
| } | |
| LBPS_DEF LBPS_S32 LBPS_PatchU32(LBPS_Serializer* s, LBPS_PatchSite site, LBPS_U32 value) { | |
| return( LBPS_PatchBytes(s, site, &value, 4) ); | |
| } | |
| LBPS_DEF void LBPS_SerializeU8(LBPS_Serializer* s, LBPS_U8* value) { | |
| if ( s->Error != kLBPS_Error_None ) { | |
| return; | |
| } | |
| if ( value == 0 ) { | |
| LBPS_SetError(s, kLBPS_Error_BadData); | |
| return; | |
| } | |
| if ( s->Mode == kLBPS_Mode_Read ) { | |
| if ( !LBPS__CanRead(s, 1) ) { | |
| return; | |
| } | |
| if ( s->IsStream ) { | |
| if ( !LBPS__StreamReadExact(s, s->Offset, value, 1) ) { | |
| return; | |
| } | |
| } | |
| else { | |
| *value = s->Ptr[0]; | |
| } | |
| } | |
| else if ( s->Mode == kLBPS_Mode_Write ) { | |
| if ( !LBPS__CanWrite(s, 1) ) { | |
| return; | |
| } | |
| if ( s->IsStream ) { | |
| if ( !LBPS__StreamWriteExact(s, s->Offset, value, 1) ) { | |
| return; | |
| } | |
| } | |
| else { | |
| s->Ptr[0] = *value; | |
| } | |
| } | |
| else { | |
| if ( !LBPS__AdvanceChecked(s, 1) ) { | |
| return; | |
| } | |
| } | |
| LBPS__Advance(s, 1); | |
| } | |
| LBPS_DEF void LBPS_SerializeS8(LBPS_Serializer* s, LBPS_S8* value) { | |
| LBPS_U8 bits = 0; | |
| if ( s->Error != kLBPS_Error_None ) { | |
| return; | |
| } | |
| if ( value == 0 ) { | |
| LBPS_SetError(s, kLBPS_Error_BadData); | |
| return; | |
| } | |
| if ( s->Mode != kLBPS_Mode_Read ) { | |
| LBPS_COPY_BYTES(&bits, value, 1); | |
| } | |
| LBPS_SerializeU8(s, &bits); | |
| if ( s->Mode == kLBPS_Mode_Read && s->Error == kLBPS_Error_None ) { | |
| LBPS_COPY_BYTES(value, &bits, 1); | |
| } | |
| } | |
| LBPS_DEF void LBPS_SerializeU16(LBPS_Serializer* s, LBPS_U16* value) { | |
| if ( s->Error != kLBPS_Error_None ) { | |
| return; | |
| } | |
| if ( value == 0 ) { | |
| LBPS_SetError(s, kLBPS_Error_BadData); | |
| return; | |
| } | |
| if ( s->Mode == kLBPS_Mode_Read ) { | |
| if ( !LBPS__CanRead(s, 2) ) { | |
| return; | |
| } | |
| if ( s->IsStream ) { | |
| if ( !LBPS__StreamReadExact(s, s->Offset, value, 2) ) { | |
| return; | |
| } | |
| } | |
| else { | |
| LBPS_COPY_BYTES(value, s->Ptr, 2); | |
| } | |
| } | |
| else if ( s->Mode == kLBPS_Mode_Write ) { | |
| if ( !LBPS__CanWrite(s, 2) ) { | |
| return; | |
| } | |
| if ( s->IsStream ) { | |
| if ( !LBPS__StreamWriteExact(s, s->Offset, value, 2) ) { | |
| return; | |
| } | |
| } | |
| else { | |
| LBPS_COPY_BYTES(s->Ptr, value, 2); | |
| } | |
| } | |
| else { | |
| if ( !LBPS__AdvanceChecked(s, 2) ) { | |
| return; | |
| } | |
| } | |
| LBPS__Advance(s, 2); | |
| } | |
| LBPS_DEF void LBPS_SerializeS16(LBPS_Serializer* s, LBPS_S16* value) { | |
| LBPS_U16 bits = 0; | |
| if ( s->Error != kLBPS_Error_None ) { | |
| return; | |
| } | |
| if ( value == 0 ) { | |
| LBPS_SetError(s, kLBPS_Error_BadData); | |
| return; | |
| } | |
| if ( s->Mode != kLBPS_Mode_Read ) { | |
| LBPS_COPY_BYTES(&bits, value, 2); | |
| } | |
| LBPS_SerializeU16(s, &bits); | |
| if ( s->Mode == kLBPS_Mode_Read && s->Error == kLBPS_Error_None ) { | |
| LBPS_COPY_BYTES(value, &bits, 2); | |
| } | |
| } | |
| LBPS_DEF void LBPS_SerializeU32(LBPS_Serializer* s, LBPS_U32* value) { | |
| if ( s->Error != kLBPS_Error_None ) { | |
| return; | |
| } | |
| if ( value == 0 ) { | |
| LBPS_SetError(s, kLBPS_Error_BadData); | |
| return; | |
| } | |
| if ( s->Mode == kLBPS_Mode_Read ) { | |
| if ( !LBPS__CanRead(s, 4) ) { | |
| return; | |
| } | |
| if ( s->IsStream ) { | |
| if ( !LBPS__StreamReadExact(s, s->Offset, value, 4) ) { | |
| return; | |
| } | |
| } | |
| else { | |
| LBPS_COPY_BYTES(value, s->Ptr, 4); | |
| } | |
| } | |
| else if ( s->Mode == kLBPS_Mode_Write ) { | |
| if ( !LBPS__CanWrite(s, 4) ) { | |
| return; | |
| } | |
| if ( s->IsStream ) { | |
| if ( !LBPS__StreamWriteExact(s, s->Offset, value, 4) ) { | |
| return; | |
| } | |
| } | |
| else { | |
| LBPS_COPY_BYTES(s->Ptr, value, 4); | |
| } | |
| } | |
| else { | |
| if ( !LBPS__AdvanceChecked(s, 4) ) { | |
| return; | |
| } | |
| } | |
| LBPS__Advance(s, 4); | |
| } | |
| LBPS_DEF void LBPS_SerializeS32(LBPS_Serializer* s, LBPS_S32* value) { | |
| LBPS_U32 bits = 0; | |
| if ( s->Error != kLBPS_Error_None ) { | |
| return; | |
| } | |
| if ( value == 0 ) { | |
| LBPS_SetError(s, kLBPS_Error_BadData); | |
| return; | |
| } | |
| if ( s->Mode != kLBPS_Mode_Read ) { | |
| LBPS_COPY_BYTES(&bits, value, 4); | |
| } | |
| LBPS_SerializeU32(s, &bits); | |
| if ( s->Mode == kLBPS_Mode_Read && s->Error == kLBPS_Error_None ) { | |
| LBPS_COPY_BYTES(value, &bits, 4); | |
| } | |
| } | |
| LBPS_DEF void LBPS_SerializeF32(LBPS_Serializer* s, LBPS_F32* value) { | |
| LBPS_U32 bits = 0; | |
| if ( s->Error != kLBPS_Error_None ) { | |
| return; | |
| } | |
| if ( value == 0 ) { | |
| LBPS_SetError(s, kLBPS_Error_BadData); | |
| return; | |
| } | |
| if ( s->Mode != kLBPS_Mode_Read ) { | |
| LBPS_COPY_BYTES(&bits, value, 4); | |
| } | |
| LBPS_SerializeU32(s, &bits); | |
| if ( s->Mode == kLBPS_Mode_Read && s->Error == kLBPS_Error_None ) { | |
| LBPS_COPY_BYTES(value, &bits, 4); | |
| } | |
| } | |
| LBPS_DEF void LBPS_SerializeBytes(LBPS_Serializer* s, void* data, LBPS_SIZE size) { | |
| if ( s->Error != kLBPS_Error_None ) { | |
| return; | |
| } | |
| if ( size == 0 ) { | |
| return; | |
| } | |
| if ( data == 0 ) { | |
| LBPS_SetError(s, kLBPS_Error_BadData); | |
| return; | |
| } | |
| if ( s->Mode == kLBPS_Mode_Read ) { | |
| if ( !LBPS__CanRead(s, size) ) { | |
| return; | |
| } | |
| if ( s->IsStream ) { | |
| if ( !LBPS__StreamReadExact(s, s->Offset, data, size) ) { | |
| return; | |
| } | |
| } | |
| else { | |
| LBPS_COPY_BYTES(data, s->Ptr, size); | |
| } | |
| } | |
| else if ( s->Mode == kLBPS_Mode_Write ) { | |
| if ( !LBPS__CanWrite(s, size) ) { | |
| return; | |
| } | |
| if ( s->IsStream ) { | |
| if ( !LBPS__StreamWriteExact(s, s->Offset, data, size) ) { | |
| return; | |
| } | |
| } | |
| else { | |
| LBPS_COPY_BYTES(s->Ptr, data, size); | |
| } | |
| } | |
| else { | |
| if ( !LBPS__AdvanceChecked(s, size) ) { | |
| return; | |
| } | |
| } | |
| LBPS__Advance(s, size); | |
| } | |
| LBPS_DEF void LBPS_Skip(LBPS_Serializer* s, LBPS_SIZE size) { | |
| LBPS_SIZE idx = 0; | |
| if ( s->Error != kLBPS_Error_None ) { | |
| return; | |
| } | |
| if ( size == 0 ) { | |
| return; | |
| } | |
| if ( s->Mode == kLBPS_Mode_Read ) { | |
| if ( !LBPS__CanRead(s, size) ) { | |
| return; | |
| } | |
| } | |
| else if ( s->Mode == kLBPS_Mode_Write ) { | |
| if ( !LBPS__CanWrite(s, size) ) { | |
| return; | |
| } | |
| if ( s->IsStream ) { | |
| if ( !LBPS__StreamWriteFill(s, size, 0) ) { | |
| return; | |
| } | |
| } | |
| else { | |
| for ( idx = 0; idx < size; ++idx ) { | |
| s->Ptr[idx] = 0; | |
| } | |
| } | |
| } | |
| else { | |
| if ( !LBPS__AdvanceChecked(s, size) ) { | |
| return; | |
| } | |
| } | |
| LBPS__Advance(s, size); | |
| } | |
| LBPS_DEF void LBPS_Align(LBPS_Serializer* s, LBPS_SIZE alignment, LBPS_U8 pad_byte) { | |
| LBPS_SIZE pad_size = 0; | |
| LBPS_SIZE idx = 0; | |
| if ( s->Error != kLBPS_Error_None ) { | |
| return; | |
| } | |
| if ( alignment == 0 ) { | |
| LBPS_SetError(s, kLBPS_Error_BadData); | |
| return; | |
| } | |
| if ( alignment == 1 ) { | |
| return; | |
| } | |
| pad_size = s->Offset % alignment; | |
| if ( pad_size != 0 ) { | |
| pad_size = alignment - pad_size; | |
| } | |
| if ( pad_size == 0 ) { | |
| return; | |
| } | |
| if ( s->Mode == kLBPS_Mode_Read ) { | |
| if ( !LBPS__CanRead(s, pad_size) ) { | |
| return; | |
| } | |
| } | |
| else if ( s->Mode == kLBPS_Mode_Write ) { | |
| if ( !LBPS__CanWrite(s, pad_size) ) { | |
| return; | |
| } | |
| if ( s->IsStream ) { | |
| if ( !LBPS__StreamWriteFill(s, pad_size, pad_byte) ) { | |
| return; | |
| } | |
| } | |
| else { | |
| for ( idx = 0; idx < pad_size; ++idx ) { | |
| s->Ptr[idx] = pad_byte; | |
| } | |
| } | |
| } | |
| else { | |
| if ( !LBPS__AdvanceChecked(s, pad_size) ) { | |
| return; | |
| } | |
| } | |
| LBPS__Advance(s, pad_size); | |
| } | |
| #ifndef LBPS_NO_64_BIT | |
| LBPS_DEF void LBPS_SerializeU64(LBPS_Serializer* s, LBPS_U64* value) { | |
| if ( s->Error != kLBPS_Error_None ) { | |
| return; | |
| } | |
| if ( value == 0 ) { | |
| LBPS_SetError(s, kLBPS_Error_BadData); | |
| return; | |
| } | |
| if ( s->Mode == kLBPS_Mode_Read ) { | |
| if ( !LBPS__CanRead(s, 8) ) { | |
| return; | |
| } | |
| if ( s->IsStream ) { | |
| if ( !LBPS__StreamReadExact(s, s->Offset, value, 8) ) { | |
| return; | |
| } | |
| } | |
| else { | |
| LBPS_COPY_BYTES(value, s->Ptr, 8); | |
| } | |
| } | |
| else if ( s->Mode == kLBPS_Mode_Write ) { | |
| if ( !LBPS__CanWrite(s, 8) ) { | |
| return; | |
| } | |
| if ( s->IsStream ) { | |
| if ( !LBPS__StreamWriteExact(s, s->Offset, value, 8) ) { | |
| return; | |
| } | |
| } | |
| else { | |
| LBPS_COPY_BYTES(s->Ptr, value, 8); | |
| } | |
| } | |
| else { | |
| if ( !LBPS__AdvanceChecked(s, 8) ) { | |
| return; | |
| } | |
| } | |
| LBPS__Advance(s, 8); | |
| } | |
| LBPS_DEF void LBPS_SerializeS64(LBPS_Serializer* s, LBPS_S64* value) { | |
| LBPS_U64 bits = 0; | |
| if ( s->Error != kLBPS_Error_None ) { | |
| return; | |
| } | |
| if ( value == 0 ) { | |
| LBPS_SetError(s, kLBPS_Error_BadData); | |
| return; | |
| } | |
| if ( s->Mode != kLBPS_Mode_Read ) { | |
| LBPS_COPY_BYTES(&bits, value, 8); | |
| } | |
| LBPS_SerializeU64(s, &bits); | |
| if ( s->Mode == kLBPS_Mode_Read && s->Error == kLBPS_Error_None ) { | |
| LBPS_COPY_BYTES(value, &bits, 8); | |
| } | |
| } | |
| LBPS_DEF void LBPS_SerializeF64(LBPS_Serializer* s, LBPS_F64* value) { | |
| LBPS_U64 bits = 0; | |
| if ( s->Error != kLBPS_Error_None ) { | |
| return; | |
| } | |
| if ( value == 0 ) { | |
| LBPS_SetError(s, kLBPS_Error_BadData); | |
| return; | |
| } | |
| if ( s->Mode != kLBPS_Mode_Read ) { | |
| LBPS_COPY_BYTES(&bits, value, 8); | |
| } | |
| LBPS_SerializeU64(s, &bits); | |
| if ( s->Mode == kLBPS_Mode_Read && s->Error == kLBPS_Error_None ) { | |
| LBPS_COPY_BYTES(value, &bits, 8); | |
| } | |
| } | |
| #endif /* LBPS_NO_64_BIT */ | |
| #endif /* OH4_LBP_SERIALIZER_IMPLEMENTATION */ | |
| #endif /* OH4_LBP_SERIALIZER_H */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment