Added extensions

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-10-27 23:58:12 +01:00
parent ad20c80215
commit 7e6b3dab21
7 changed files with 788 additions and 32 deletions
+26
View File
@@ -167,6 +167,32 @@ Supported encodings: `#PB_UTF8` (default, per spec), `#PB_Ascii`, `#PB_Unicode`
See: `MsgPackLastError`
### Extensions
`ExtType.b` is the signed ext type, `-128` to `-1` being reserved by the spec but not rejected. \
Size errors reuse the `#MsgPack_Error_BinaryDataSize*` codes.
* `MsgPackWriteExtension.MsgPack_ErrorCode(*MsgPackData.MsgPackData, ExtType.b, *Buffer, BufferSize.q)` \
Writes `BufferSize` bytes from `*Buffer` using fixext for 1/2/4/8/16 bytes, or the smallest ext format code otherwise. \
Returns `#MsgPack_Error_Success` on success, something else otherwise.
* `MsgPackWriteExt8/16/32.MsgPack_ErrorCode(*MsgPackData.MsgPackData, ExtType.b, *Buffer, BufferSize.q)` \
Same as `MsgPackWriteExtension`, but forces the given format code, even if a smaller one fits.
* `MsgPackWriteFixExt1/2/4/8/16.MsgPack_ErrorCode(*MsgPackData.MsgPackData, ExtType.b, *Buffer)` \
Writes the 1/2/4/8/16 bytes at `*Buffer` as the given fixext.
* `MsgPackGetExtensionType.b(*MsgPackData.MsgPackData)` \
Returns the type of the ext field at the current internal offset, without moving it. \
Returns `0` on errors and sets the internal error code. \
See: `MsgPackLastError`
* `MsgPackReadExtension.q(*MsgPackData.MsgPackData, *OutputBuffer, OutputBufferSize.q)` \
Same as `MsgPackReadBinary`, for the data of ext fields.
* `MsgPackCopyExtension.i(*MsgPackData.MsgPackData)` \
Same as `MsgPackCopyBinary`, for the data of ext fields.
## Remarks
* Never touch the `MsgPackData` structure unless you **absolutely** have to.
+1
View File
@@ -30,4 +30,5 @@ XIncludeFile "./_Literals.pbi"
XIncludeFile "./_Binary.pbi"
XIncludeFile "./_Primitives.pbi"
XIncludeFile "./_Strings.pbi"
XIncludeFile "./_Extensions.pbi"
XIncludeFile "./_Iterators.pbi"
+77 -31
View File
@@ -21,12 +21,14 @@
; Sizes are all unsigned 8/16/32, using Q fixes any potential issues !
; The "sized field" helpers below handle every `[FormatCode][Size][Data]` layout
; shared by the str and bin families.
; shared by the str, bin and ext families.
; The `SizeFieldByteLength` parameter is the number of bytes used to store the size:
; * `0` - Size is stored in the format code's lower 5 bits. (FixStr)
; * `1` - Str8, Bin8
; * `2` - Str16, Bin16
; * `4` - Str32, Bin32
; Or implied by the format code. (FixExt1/2/4/8/16)
; * `1` - Str8, Bin8, Ext8
; * `2` - Str16, Bin16, Ext16
; * `4` - Str32, Bin32, Ext32
; The ext family also has a 1 byte type between its size and its data.
@@ -51,9 +53,11 @@ XIncludeFile "./_Primitives.pbi"
; Validates `DataSize`, reserves space for the whole field, writes its format code
; and size, and moves the offset right after them.
; The caller is expected to write `DataSize` bytes of data and move the offset afterward.
; `ExtraHeaderSize` more bytes are reserved right after the size, for the ext type.
; The caller is expected to write the extra header bytes and `DataSize` bytes of data,
; and move the offset afterward.
; Sets the structure's error code and returns it.
Procedure.MsgPack_ErrorCode _MsgPackWriteSizedFieldHeader(*MsgPackData.MsgPackData, FormatCode.MsgPack_FormatCode, SizeFieldByteLength.a, DataSize.q)
Procedure.MsgPack_ErrorCode _MsgPackWriteSizedFieldHeader(*MsgPackData.MsgPackData, FormatCode.MsgPack_FormatCode, SizeFieldByteLength.a, DataSize.q, ExtraHeaderSize.a = 0)
Protected MaxDataSize.q
Protected ErrorCode.MsgPack_ErrorCode
@@ -89,7 +93,7 @@ Procedure.MsgPack_ErrorCode _MsgPackWriteSizedFieldHeader(*MsgPackData.MsgPackDa
ProcedureReturn #MsgPack_Error_BinaryDataSizeIsTooBig
EndIf
ErrorCode = _MsgPackReserveWrite(*MsgPackData, 1 + SizeFieldByteLength + DataSize)
ErrorCode = _MsgPackReserveWrite(*MsgPackData, 1 + SizeFieldByteLength + ExtraHeaderSize + DataSize)
If ErrorCode <> #MsgPack_Error_Success
ProcedureReturn ErrorCode
EndIf
@@ -132,14 +136,17 @@ Procedure.MsgPack_ErrorCode _MsgPackWriteSizedField(*MsgPackData.MsgPackData, Fo
EndProcedure
; Reads the header of a str or bin field at the current offset, without moving it.
; `ExpectedDataType` must be `#MsgPack_DataTypes_String` or `#MsgPack_DataTypes_Binary`.
; On success, `*HeaderSize` receives the format code + size field length, and `*DataSize`
; the data's length, both guaranteed to be within the buffer.
; Reads the header of a str, bin or ext field at the current offset, without moving it.
; `ExpectedDataType` must be `#MsgPack_DataTypes_String`, `#MsgPack_DataTypes_Binary`
; or `#MsgPack_DataTypes_Extension`.
; On success, `*HeaderSize` receives the format code + size field (+ ext type) length,
; and `*DataSize` the data's length, both guaranteed to be within the buffer.
; For ext fields, the type is the header's last byte.
; Sets the structure's error code and returns it.
Procedure.MsgPack_ErrorCode _MsgPackReadSizedFieldHeader(*MsgPackData.MsgPackData, ExpectedDataType.MsgPack_DataType, *HeaderSize.Integer, *DataSize.Quad)
Protected FormatCode.MsgPack_FormatCode
Protected SizeFieldByteLength.a
Protected ExtTypeByteLength.a = 0
Protected DataSize.q
CompilerIf Not #MsgPack_DisableNullChecks
@@ -166,17 +173,34 @@ Procedure.MsgPack_ErrorCode _MsgPackReadSizedFieldHeader(*MsgPackData.MsgPackDat
Case #MsgPack_FormatCode_FixStr:
SizeFieldByteLength = 0
Case #MsgPack_FormatCode_Str8,
#MsgPack_FormatCode_Bin8:
#MsgPack_FormatCode_Bin8,
#MsgPack_FormatCode_Ext8:
SizeFieldByteLength = 1
Case #MsgPack_FormatCode_Str16,
#MsgPack_FormatCode_Bin16:
#MsgPack_FormatCode_Bin16,
#MsgPack_FormatCode_Ext16:
SizeFieldByteLength = 2
Case #MsgPack_FormatCode_Str32,
#MsgPack_FormatCode_Bin32:
#MsgPack_FormatCode_Bin32,
#MsgPack_FormatCode_Ext32:
SizeFieldByteLength = 4
Case #MsgPack_FormatCode_FixExt1:
SizeFieldByteLength = 0 : DataSize = 1
Case #MsgPack_FormatCode_FixExt2:
SizeFieldByteLength = 0 : DataSize = 2
Case #MsgPack_FormatCode_FixExt4:
SizeFieldByteLength = 0 : DataSize = 4
Case #MsgPack_FormatCode_FixExt8:
SizeFieldByteLength = 0 : DataSize = 8
Case #MsgPack_FormatCode_FixExt16:
SizeFieldByteLength = 0 : DataSize = 16
EndSelect
If Not _MsgPackHasSpaceLeft(*MsgPackData, 1 + SizeFieldByteLength)
If ExpectedDataType = #MsgPack_DataTypes_Extension
ExtTypeByteLength = 1
EndIf
If Not _MsgPackHasSpaceLeft(*MsgPackData, 1 + SizeFieldByteLength + ExtTypeByteLength)
Debug("End of buffer reached, cannot read the field's size !")
*MsgPackData\LastError = #MsgPack_Error_BufferTooSmall
ProcedureReturn #MsgPack_Error_BufferTooSmall
@@ -184,7 +208,10 @@ Procedure.MsgPack_ErrorCode _MsgPackReadSizedFieldHeader(*MsgPackData.MsgPackDat
Select SizeFieldByteLength
Case 0:
DataSize = PeekA(*MsgPackData\Buffer + *MsgPackData\BufferOffset) & %00011111
; FixExt sizes were already set above.
If FormatCode = #MsgPack_FormatCode_FixStr
DataSize = PeekA(*MsgPackData\Buffer + *MsgPackData\BufferOffset) & %00011111
EndIf
Case 1:
DataSize = PeekA(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1)
Case 2:
@@ -193,13 +220,13 @@ Procedure.MsgPack_ErrorCode _MsgPackReadSizedFieldHeader(*MsgPackData.MsgPackDat
DataSize = EndianSwapI32(PeekL(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1)) & $FFFFFFFF
EndSelect
If Not _MsgPackHasSpaceLeft(*MsgPackData, 1 + SizeFieldByteLength + DataSize)
If Not _MsgPackHasSpaceLeft(*MsgPackData, 1 + SizeFieldByteLength + ExtTypeByteLength + DataSize)
Debug("End of buffer reached, cannot read the field's data !")
*MsgPackData\LastError = #MsgPack_Error_BufferTooSmall
ProcedureReturn #MsgPack_Error_BufferTooSmall
EndIf
*HeaderSize\i = 1 + SizeFieldByteLength
*HeaderSize\i = 1 + SizeFieldByteLength + ExtTypeByteLength
*DataSize\q = DataSize
*MsgPackData\LastError = #MsgPack_Error_Success
@@ -207,18 +234,15 @@ Procedure.MsgPack_ErrorCode _MsgPackReadSizedFieldHeader(*MsgPackData.MsgPackDat
EndProcedure
;-> Getters
; Copies the bin field at the current offset into `*OutputBuffer`.
; Copies the data of the bin or ext field at the current offset into `*OutputBuffer`.
; Returns: The number of bytes copied.
; OnError: Returns `#MsgPack_InvalidSize` and sets the `MsgPackData\LastError` field.
; The offset is left untouched, so a bigger buffer can be given afterward.
Procedure.q MsgPackReadBinary(*MsgPackData.MsgPackData, *OutputBuffer, OutputBufferSize.q)
Procedure.q _MsgPackReadSizedFieldData(*MsgPackData.MsgPackData, ExpectedDataType.MsgPack_DataType, *OutputBuffer, OutputBufferSize.q)
Protected HeaderSize.i
Protected DataSize.q
If _MsgPackReadSizedFieldHeader(*MsgPackData, #MsgPack_DataTypes_Binary, @HeaderSize, @DataSize) <> #MsgPack_Error_Success
If _MsgPackReadSizedFieldHeader(*MsgPackData, ExpectedDataType, @HeaderSize, @DataSize) <> #MsgPack_Error_Success
ProcedureReturn #MsgPack_InvalidSize
EndIf
@@ -246,24 +270,24 @@ Procedure.q MsgPackReadBinary(*MsgPackData.MsgPackData, *OutputBuffer, OutputBuf
EndProcedure
; Copies the bin field at the current offset into a new buffer.
; Copies the data of the bin or ext field at the current offset into a new buffer.
; Returns: A buffer from `AllocateMemory()` that the caller must free with `FreeMemory()`.
; Its size can be obtained with `MemorySize()`.
; Empty bin fields return `#Null` with `MsgPackData\LastError` set to `#MsgPack_Error_Success`.
; Empty fields return `#Null` with `MsgPackData\LastError` set to `#MsgPack_Error_Success`.
; OnError: Returns `#Null` and sets the `MsgPackData\LastError` field.
Procedure.i MsgPackCopyBinary(*MsgPackData.MsgPackData)
Procedure.i _MsgPackCopySizedFieldData(*MsgPackData.MsgPackData, ExpectedDataType.MsgPack_DataType)
Protected HeaderSize.i
Protected DataSize.q
Protected *ReturnedValue = #Null
If _MsgPackReadSizedFieldHeader(*MsgPackData, #MsgPack_DataTypes_Binary, @HeaderSize, @DataSize) <> #MsgPack_Error_Success
If _MsgPackReadSizedFieldHeader(*MsgPackData, ExpectedDataType, @HeaderSize, @DataSize) <> #MsgPack_Error_Success
ProcedureReturn #Null
EndIf
If DataSize > 0
*ReturnedValue = AllocateMemory(DataSize, #PB_Memory_NoClear)
If Not *ReturnedValue
Debug("Failed to allocate memory for the copied binary !")
Debug("Failed to allocate memory for the copied data !")
*MsgPackData\LastError = #MsgPack_Error_FailedToAllocateBuffer
ProcedureReturn #Null
EndIf
@@ -278,6 +302,28 @@ EndProcedure
;-> Getters
; Copies the bin field at the current offset into `*OutputBuffer`.
; Returns: The number of bytes copied.
; OnError: Returns `#MsgPack_InvalidSize` and sets the `MsgPackData\LastError` field.
; The offset is left untouched, so a bigger buffer can be given afterward.
Procedure.q MsgPackReadBinary(*MsgPackData.MsgPackData, *OutputBuffer, OutputBufferSize.q)
ProcedureReturn _MsgPackReadSizedFieldData(*MsgPackData, #MsgPack_DataTypes_Binary, *OutputBuffer, OutputBufferSize)
EndProcedure
; Copies the bin field at the current offset into a new buffer.
; Returns: A buffer from `AllocateMemory()` that the caller must free with `FreeMemory()`.
; Its size can be obtained with `MemorySize()`.
; Empty bin fields return `#Null` with `MsgPackData\LastError` set to `#MsgPack_Error_Success`.
; OnError: Returns `#Null` and sets the `MsgPackData\LastError` field.
Procedure.i MsgPackCopyBinary(*MsgPackData.MsgPackData)
ProcedureReturn _MsgPackCopySizedFieldData(*MsgPackData, #MsgPack_DataTypes_Binary)
EndProcedure
;-> Setters
; Writes `BufferSize` bytes from `*Buffer` as a bin8.
+190 -1
View File
@@ -9,7 +9,7 @@
; * //PureBasic 5.73 LTS (x86/x64)
; * //PureBasic 6.21 (x86/x64)
; * //PureBasic 6.21 C Backend (arm64)
;
;
; ==- Links & License -===========================
; License: CC0 1.0 Universal (Public Domain)
; GitHub: https://github.com/aziascreations/PB-MessagePack
@@ -21,6 +21,12 @@
; Specification:
; * https://github.com/msgpack/msgpack/blob/master/spec.md
; The ext type is a signed byte, `-128` to `-1` being reserved by the spec. (`-1` = timestamp)
; Reserved types are not rejected, so predefined types can be written with these procedures.
; FixExt fields have no size field, their data size is implied by the format code.
; See "_Binary.pbi" for the shared "sized field" helpers.
; ------------------------------------------------------------------------------
;- Compiler directive
@@ -30,4 +36,187 @@ CompilerIf #PB_Compiler_IsMainFile
CompilerEndIf
XIncludeFile "./_Commons.pbi"
XIncludeFile "./_Primitives.pbi"
XIncludeFile "./_Binary.pbi"
; ------------------------------------------------------------------------------
;- Procedures
;-> Internal helpers
; Writes a full ext field by copying `DataSize` bytes from `*Data`.
; `SizeFieldByteLength` set to `0` indicates a FixExt whose implied size must match `DataSize`.
; Sets the structure's error code and returns it.
Procedure.MsgPack_ErrorCode _MsgPackWriteExtField(*MsgPackData.MsgPackData, FormatCode.MsgPack_FormatCode, SizeFieldByteLength.a, ExtType.b, *Data, DataSize.q)
Protected ErrorCode.MsgPack_ErrorCode
Protected FixExtSize.q
CompilerIf Not #MsgPack_DisableNullChecks
If Not *MsgPackData
DebuggerError("A #Null MsgPackData pointer was passed !")
ProcedureReturn #MsgPack_Error_NullPointerGiven
EndIf
CompilerEndIf
If SizeFieldByteLength = 0
Select FormatCode
Case #MsgPack_FormatCode_FixExt1 : FixExtSize = 1
Case #MsgPack_FormatCode_FixExt2 : FixExtSize = 2
Case #MsgPack_FormatCode_FixExt4 : FixExtSize = 4
Case #MsgPack_FormatCode_FixExt8 : FixExtSize = 8
Case #MsgPack_FormatCode_FixExt16 : FixExtSize = 16
Default
FixExtSize = #MsgPack_InvalidSize
EndSelect
If DataSize <> FixExtSize
DebuggerError("Invalid FixExt size: " + Str(DataSize))
*MsgPackData\LastError = #MsgPack_Error_InvalidValue
ProcedureReturn #MsgPack_Error_InvalidValue
EndIf
ErrorCode = _MsgPackReserveWrite(*MsgPackData, 2 + DataSize)
If ErrorCode <> #MsgPack_Error_Success
ProcedureReturn ErrorCode
EndIf
PokeA(*MsgPackData\Buffer + *MsgPackData\BufferOffset, FormatCode)
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + 1
Else
ErrorCode = _MsgPackWriteSizedFieldHeader(*MsgPackData, FormatCode, SizeFieldByteLength, DataSize, 1)
If ErrorCode <> #MsgPack_Error_Success
ProcedureReturn ErrorCode
EndIf
EndIf
PokeB(*MsgPackData\Buffer + *MsgPackData\BufferOffset, ExtType)
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + 1
If DataSize > 0
CopyMemory(*Data, *MsgPackData\Buffer + *MsgPackData\BufferOffset, DataSize)
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + DataSize
EndIf
ProcedureReturn #MsgPack_Error_Success
EndProcedure
;-> Getters
; Reads the type of the ext field at the current offset, without moving it.
; Returns: The ext type.
; OnError: Returns `0` and sets the `MsgPackData\LastError` field.
Procedure.b MsgPackGetExtensionType(*MsgPackData.MsgPackData)
Protected HeaderSize.i
Protected DataSize.q
If _MsgPackReadSizedFieldHeader(*MsgPackData, #MsgPack_DataTypes_Extension, @HeaderSize, @DataSize) <> #MsgPack_Error_Success
ProcedureReturn 0
EndIf
ProcedureReturn PeekB(*MsgPackData\Buffer + *MsgPackData\BufferOffset + HeaderSize - 1)
EndProcedure
; Copies the data of the ext field at the current offset into `*OutputBuffer`.
; Its type can be read beforehand with `MsgPackGetExtensionType()`.
; Returns: The number of bytes copied.
; OnError: Returns `#MsgPack_InvalidSize` and sets the `MsgPackData\LastError` field.
; The offset is left untouched, so a bigger buffer can be given afterward.
Procedure.q MsgPackReadExtension(*MsgPackData.MsgPackData, *OutputBuffer, OutputBufferSize.q)
ProcedureReturn _MsgPackReadSizedFieldData(*MsgPackData, #MsgPack_DataTypes_Extension, *OutputBuffer, OutputBufferSize)
EndProcedure
; Copies the data of the ext field at the current offset into a new buffer.
; Its type can be read beforehand with `MsgPackGetExtensionType()`.
; Returns: A buffer from `AllocateMemory()` that the caller must free with `FreeMemory()`.
; Its size can be obtained with `MemorySize()`.
; Empty ext fields return `#Null` with `MsgPackData\LastError` set to `#MsgPack_Error_Success`.
; OnError: Returns `#Null` and sets the `MsgPackData\LastError` field.
Procedure.i MsgPackCopyExtension(*MsgPackData.MsgPackData)
ProcedureReturn _MsgPackCopySizedFieldData(*MsgPackData, #MsgPack_DataTypes_Extension)
EndProcedure
;-> Setters
; Writes `BufferSize` bytes from `*Buffer` as an ext8 of type `ExtType`, even if a smaller format code fits.
; Returns `#MsgPack_Error_Success` on success, something else otherwise.
Procedure.MsgPack_ErrorCode MsgPackWriteExt8(*MsgPackData.MsgPackData, ExtType.b, *Buffer, BufferSize.q)
ProcedureReturn _MsgPackWriteExtField(*MsgPackData, #MsgPack_FormatCode_Ext8, 1, ExtType, *Buffer, BufferSize)
EndProcedure
; Writes `BufferSize` bytes from `*Buffer` as an ext16 of type `ExtType`, even if a smaller format code fits.
; Returns `#MsgPack_Error_Success` on success, something else otherwise.
Procedure.MsgPack_ErrorCode MsgPackWriteExt16(*MsgPackData.MsgPackData, ExtType.b, *Buffer, BufferSize.q)
ProcedureReturn _MsgPackWriteExtField(*MsgPackData, #MsgPack_FormatCode_Ext16, 2, ExtType, *Buffer, BufferSize)
EndProcedure
; Writes `BufferSize` bytes from `*Buffer` as an ext32 of type `ExtType`, even if a smaller format code fits.
; Returns `#MsgPack_Error_Success` on success, something else otherwise.
Procedure.MsgPack_ErrorCode MsgPackWriteExt32(*MsgPackData.MsgPackData, ExtType.b, *Buffer, BufferSize.q)
ProcedureReturn _MsgPackWriteExtField(*MsgPackData, #MsgPack_FormatCode_Ext32, 4, ExtType, *Buffer, BufferSize)
EndProcedure
; Writes `BufferSize` bytes from `*Buffer` as an ext of type `ExtType` using the smallest
; format code that fits.
; Returns `#MsgPack_Error_Success` on success, something else otherwise.
Procedure.MsgPack_ErrorCode MsgPackWriteExtension(*MsgPackData.MsgPackData, ExtType.b, *Buffer, BufferSize.q)
Select BufferSize
Case 1
ProcedureReturn _MsgPackWriteExtField(*MsgPackData, #MsgPack_FormatCode_FixExt1, 0, ExtType, *Buffer, BufferSize)
Case 2
ProcedureReturn _MsgPackWriteExtField(*MsgPackData, #MsgPack_FormatCode_FixExt2, 0, ExtType, *Buffer, BufferSize)
Case 4
ProcedureReturn _MsgPackWriteExtField(*MsgPackData, #MsgPack_FormatCode_FixExt4, 0, ExtType, *Buffer, BufferSize)
Case 8
ProcedureReturn _MsgPackWriteExtField(*MsgPackData, #MsgPack_FormatCode_FixExt8, 0, ExtType, *Buffer, BufferSize)
Case 16
ProcedureReturn _MsgPackWriteExtField(*MsgPackData, #MsgPack_FormatCode_FixExt16, 0, ExtType, *Buffer, BufferSize)
EndSelect
If BufferSize <= 255
ProcedureReturn MsgPackWriteExt8(*MsgPackData, ExtType, *Buffer, BufferSize)
ElseIf BufferSize <= 65535
ProcedureReturn MsgPackWriteExt16(*MsgPackData, ExtType, *Buffer, BufferSize)
Else
; Sizes above `(2^32)-1` are rejected by the helper.
ProcedureReturn MsgPackWriteExt32(*MsgPackData, ExtType, *Buffer, BufferSize)
EndIf
EndProcedure
; ------------------------------------------------------------------------------
;- Macros
; Writes the 1/2/4/8/16 bytes at `Buffer` as a FixExt of type `ExtType`.
; Returns `#MsgPack_Error_Success` on success, something else otherwise.
Macro MsgPackWriteFixExt1(MsgPackData, ExtType, Buffer)
_MsgPackWriteExtField(MsgPackData, #MsgPack_FormatCode_FixExt1, 0, ExtType, Buffer, 1)
EndMacro
Macro MsgPackWriteFixExt2(MsgPackData, ExtType, Buffer)
_MsgPackWriteExtField(MsgPackData, #MsgPack_FormatCode_FixExt2, 0, ExtType, Buffer, 2)
EndMacro
Macro MsgPackWriteFixExt4(MsgPackData, ExtType, Buffer)
_MsgPackWriteExtField(MsgPackData, #MsgPack_FormatCode_FixExt4, 0, ExtType, Buffer, 4)
EndMacro
Macro MsgPackWriteFixExt8(MsgPackData, ExtType, Buffer)
_MsgPackWriteExtField(MsgPackData, #MsgPack_FormatCode_FixExt8, 0, ExtType, Buffer, 8)
EndMacro
Macro MsgPackWriteFixExt16(MsgPackData, ExtType, Buffer)
_MsgPackWriteExtField(MsgPackData, #MsgPack_FormatCode_FixExt16, 0, ExtType, Buffer, 16)
EndMacro
@@ -0,0 +1,188 @@
EnableExplicit
XIncludeFile "../_UnitTesting.pbi"
XIncludeFile "../../Includes/MessagePack.pbi"
XIncludeFile "../_TestBuffers.pbi"
; Note: Only covers errors that don't call `DebuggerError()`.
; See "TestExtensionsFatalErrors.pb" for the others.
; Setup
Define *MsgPack.MsgPackData
Define *Source = AllocatePattern(16)
Define *Output = AllocateMemory(16)
; Sizes past each format code's limit, checked before anything is allocated
*MsgPack = MsgPackCreate(8)
AssertEquals(MsgPackWriteExt8(*MsgPack, 1, *Source, 256), #MsgPack_Error_BinaryDataSizeIsTooBig, "Ext8 rejects 256 bytes")
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_BinaryDataSizeIsTooBig, "Ext8 too big error is set")
AssertEquals(MsgPackWriteExt16(*MsgPack, 1, *Source, 65536), #MsgPack_Error_BinaryDataSizeIsTooBig, "Ext16 rejects 65536 bytes")
AssertEquals(MsgPackWriteExt32(*MsgPack, 1, *Source, 4294967296), #MsgPack_Error_BinaryDataSizeIsTooBig, "Ext32 rejects 2^32 bytes")
AssertEquals(MsgPackWriteExtension(*MsgPack, 1, *Source, 4294967296), #MsgPack_Error_BinaryDataSizeIsTooBig, "Extension rejects 2^32 bytes")
AssertEquals(MsgPackWriteExt8(*MsgPack, 1, *Source, -1), #MsgPack_Error_BinaryDataSizeIsNegative, "Ext8 rejects negative sizes")
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_BinaryDataSizeIsNegative, "Negative size error is set")
AssertEquals(MsgPackWriteExt16(*MsgPack, 1, *Source, -1), #MsgPack_Error_BinaryDataSizeIsNegative, "Ext16 rejects negative sizes")
AssertEquals(MsgPackWriteExt32(*MsgPack, 1, *Source, -1), #MsgPack_Error_BinaryDataSizeIsNegative, "Ext32 rejects negative sizes")
AssertEquals(MsgPackWriteExtension(*MsgPack, 1, *Source, -1), #MsgPack_Error_BinaryDataSizeIsNegative, "Extension rejects negative sizes")
AssertEquals(*MsgPack\BufferOffset, 0, "Offset is untouched by rejected writes")
AssertEquals(*MsgPack\BufferSize, 8, "Buffer wasn't grown by rejected writes")
AssertEquals(MsgPackWriteExt8(*MsgPack, 1, *Source, 3), #MsgPack_Error_Success, "Wrote ext8 after rejected writes")
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_Success, "Successful write cleared the error")
MsgPackFree(*MsgPack, #True)
; Buffers that can't grow
*MsgPack = WrapHex("0000000000")
AssertEquals(MsgPackWriteFixExt4(*MsgPack, 1, *Source), #MsgPack_Error_CannotGrowBufferDueToConfig, "fixext4's 6 bytes don't fit in 5")
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_CannotGrowBufferDueToConfig, "Growth error is set")
AssertEquals(MsgPackWriteExt8(*MsgPack, 1, *Source, 3), #MsgPack_Error_CannotGrowBufferDueToConfig, "ext8's 6 bytes don't fit in 5")
AssertEquals(*MsgPack\BufferOffset, 0, "Offset is untouched by the failed writes")
AssertEqualsA(PeekA(*MsgPack\Buffer), $00, "Nothing was written by the failed writes")
AssertEquals(MsgPackWriteExt8(*MsgPack, 1, *Source, 2), #MsgPack_Error_Success, "ext8's 5 bytes fit exactly")
AssertEquals(*MsgPack\BufferOffset, 5, "Offset is at the end")
FreeWrappedHex(*MsgPack)
*MsgPack = WrapHex("00000000")
AssertEquals(MsgPackWriteFixExt2(*MsgPack, 1, *Source), #MsgPack_Error_Success, "fixext2's 4 bytes fit exactly")
AssertEquals(*MsgPack\BufferOffset, 4, "Offset is at the end")
FreeWrappedHex(*MsgPack)
; Reading at the end
*MsgPack = WrapHex("D40100")
*MsgPack\BufferOffset = 3
AssertEqualsQ(MsgPackReadExtension(*MsgPack, *Output, 16), #MsgPack_InvalidSize, "Read at the end fails")
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_AtOrPastTheEndOfBuffer, "Read at the end error is set")
AssertEquals(MsgPackCopyExtension(*MsgPack), #Null, "Copy at the end fails")
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_AtOrPastTheEndOfBuffer, "Copy at the end error is set")
AssertEqualsB(MsgPackGetExtensionType(*MsgPack), 0, "Type at the end fails")
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_AtOrPastTheEndOfBuffer, "Type at the end error is set")
FreeWrappedHex(*MsgPack)
; Wrong format codes, in both directions
*MsgPack = WrapHex("C4020102")
AssertEqualsQ(MsgPackReadExtension(*MsgPack, *Output, 16), #MsgPack_InvalidSize, "Reading a bin as an ext fails")
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_InvalidFormatCode, "Bin as ext read error is set")
AssertEquals(MsgPackCopyExtension(*MsgPack), #Null, "Copying a bin as an ext fails")
AssertEqualsB(MsgPackGetExtensionType(*MsgPack), 0, "Getting the type of a bin fails")
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_InvalidFormatCode, "Bin type error is set")
AssertEquals(*MsgPack\BufferOffset, 0, "Offset is untouched by the wrong format code")
FreeWrappedHex(*MsgPack)
*MsgPack = WrapHex("C702050102")
AssertEqualsQ(MsgPackReadBinary(*MsgPack, *Output, 16), #MsgPack_InvalidSize, "Reading an ext8 as a bin fails")
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_InvalidFormatCode, "Ext8 as bin error is set")
Assert(Bool(MsgPackReadString(*MsgPack) = ""), "Reading an ext8 as a str fails")
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_InvalidFormatCode, "Ext8 as str error is set")
FreeWrappedHex(*MsgPack)
*MsgPack = WrapHex("D40501")
AssertEqualsQ(MsgPackReadBinary(*MsgPack, *Output, 16), #MsgPack_InvalidSize, "Reading a fixext1 as a bin fails")
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_InvalidFormatCode, "Fixext1 as bin error is set")
FreeWrappedHex(*MsgPack)
*MsgPack = WrapHex("A3616263")
AssertEqualsQ(MsgPackReadExtension(*MsgPack, *Output, 16), #MsgPack_InvalidSize, "Reading a str as an ext fails")
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_InvalidFormatCode, "Str as ext error is set")
FreeWrappedHex(*MsgPack)
; Truncated size and type fields
*MsgPack = WrapHex("C7")
AssertEqualsQ(MsgPackReadExtension(*MsgPack, *Output, 16), #MsgPack_InvalidSize, "Ext8 without its size fails")
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_BufferTooSmall, "Ext8 without its size error is set")
FreeWrappedHex(*MsgPack)
*MsgPack = WrapHex("C700")
AssertEqualsQ(MsgPackReadExtension(*MsgPack, *Output, 16), #MsgPack_InvalidSize, "Empty ext8 without its type fails")
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_BufferTooSmall, "Empty ext8 without its type error is set")
AssertEqualsB(MsgPackGetExtensionType(*MsgPack), 0, "Getting a missing type fails")
FreeWrappedHex(*MsgPack)
*MsgPack = WrapHex("C80000")
AssertEqualsQ(MsgPackReadExtension(*MsgPack, *Output, 16), #MsgPack_InvalidSize, "Empty ext16 without its type fails")
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_BufferTooSmall, "Empty ext16 without its type error is set")
FreeWrappedHex(*MsgPack)
*MsgPack = WrapHex("C9000000")
AssertEqualsQ(MsgPackReadExtension(*MsgPack, *Output, 16), #MsgPack_InvalidSize, "Ext32 with 3/4 of its size fails")
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_BufferTooSmall, "Ext32 with 3/4 of its size error is set")
FreeWrappedHex(*MsgPack)
*MsgPack = WrapHex("D4")
AssertEqualsQ(MsgPackReadExtension(*MsgPack, *Output, 16), #MsgPack_InvalidSize, "Fixext1 without its type fails")
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_BufferTooSmall, "Fixext1 without its type error is set")
FreeWrappedHex(*MsgPack)
; Truncated data
*MsgPack = WrapHex("D6FF0000")
AssertEqualsQ(MsgPackReadExtension(*MsgPack, *Output, 16), #MsgPack_InvalidSize, "Fixext4 missing data fails")
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_BufferTooSmall, "Fixext4 missing data error is set")
AssertEquals(MsgPackCopyExtension(*MsgPack), #Null, "Copying a fixext4 missing data fails")
AssertEquals(*MsgPack\BufferOffset, 0, "Offset is untouched by the truncated data")
FreeWrappedHex(*MsgPack)
*MsgPack = WrapHex("C705016162")
AssertEqualsQ(MsgPackReadExtension(*MsgPack, *Output, 16), #MsgPack_InvalidSize, "Ext8 missing data fails")
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_BufferTooSmall, "Ext8 missing data error is set")
FreeWrappedHex(*MsgPack)
; The type byte must not be counted as data.
*MsgPack = WrapHex("C70201AA")
AssertEqualsQ(MsgPackReadExtension(*MsgPack, *Output, 16), #MsgPack_InvalidSize, "Ext8 with its type as the last data byte fails")
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_BufferTooSmall, "Type isn't counted as data")
FreeWrappedHex(*MsgPack)
; The ext32 size is unsigned, `0xFFFFFFFF` must not be read as `-1`.
*MsgPack = WrapHex("C9FFFFFFFF0100")
AssertEqualsQ(MsgPackReadExtension(*MsgPack, *Output, 16), #MsgPack_InvalidSize, "Ext32 with a 0xFFFFFFFF size fails")
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_BufferTooSmall, "Ext32 size is treated as unsigned")
FreeWrappedHex(*MsgPack)
; Output buffer too small, then retried
*MsgPack = WrapHex("C70307010203")
AssertEqualsQ(MsgPackReadExtension(*MsgPack, *Output, 2), #MsgPack_InvalidSize, "3 bytes don't fit in 2")
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_OutputBufferTooSmall, "Output too small error is set")
AssertEquals(*MsgPack\BufferOffset, 0, "Offset is untouched so the read can be retried")
AssertEqualsB(MsgPackGetExtensionType(*MsgPack), 7, "Type is still readable")
AssertEqualsQ(MsgPackReadExtension(*MsgPack, *Output, 3), 3, "Retried with an exactly sized buffer")
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_Success, "Retry cleared the error")
AssertEqualsA(PeekA(*Output), $01, "Retried data byte 1")
AssertEqualsA(PeekA(*Output + 1), $02, "Retried data byte 2")
AssertEqualsA(PeekA(*Output + 2), $03, "Retried data byte 3")
AssertEquals(*MsgPack\BufferOffset, 6, "Offset is at the end")
FreeWrappedHex(*MsgPack)
; Ending
FreeMemory(*Source)
FreeMemory(*Output)
If FailedUnitTests
End 1
EndIf
Debug "Done !"
End 0
@@ -0,0 +1,51 @@
EnableExplicit
XIncludeFile "../_UnitTesting.pbi"
XIncludeFile "../../Includes/MessagePack.pbi"
XIncludeFile "../_TestBuffers.pbi"
; /!\ Every case here calls `DebuggerError()`, which halts the program under the debugger.
; Run it without the debugger and check the exit code.
; Setup
Define *MsgPack.MsgPackData
Define *Source = AllocatePattern(16)
Define *Output = AllocateMemory(16)
; Null MsgPack pointers
AssertEquals(MsgPackWriteExt8(#Null, 1, *Source, 1), #MsgPack_Error_NullPointerGiven, "MsgPackWriteExt8 is given #Null")
AssertEquals(MsgPackWriteExt16(#Null, 1, *Source, 1), #MsgPack_Error_NullPointerGiven, "MsgPackWriteExt16 is given #Null")
AssertEquals(MsgPackWriteExt32(#Null, 1, *Source, 1), #MsgPack_Error_NullPointerGiven, "MsgPackWriteExt32 is given #Null")
AssertEquals(MsgPackWriteExtension(#Null, 1, *Source, 1), #MsgPack_Error_NullPointerGiven, "MsgPackWriteExtension is given #Null for a fixext")
AssertEquals(MsgPackWriteExtension(#Null, 1, *Source, 3), #MsgPack_Error_NullPointerGiven, "MsgPackWriteExtension is given #Null for an ext8")
AssertEquals(MsgPackWriteFixExt1(#Null, 1, *Source), #MsgPack_Error_NullPointerGiven, "MsgPackWriteFixExt1 is given #Null")
AssertEquals(MsgPackWriteFixExt16(#Null, 1, *Source), #MsgPack_Error_NullPointerGiven, "MsgPackWriteFixExt16 is given #Null")
AssertEqualsQ(MsgPackReadExtension(#Null, *Output, 16), #MsgPack_InvalidSize, "MsgPackReadExtension is given #Null")
AssertEquals(MsgPackCopyExtension(#Null), #Null, "MsgPackCopyExtension is given #Null")
AssertEqualsB(MsgPackGetExtensionType(#Null), 0, "MsgPackGetExtensionType is given #Null")
; Null output buffer with data to copy
*MsgPack = WrapHex("D5010102")
AssertEqualsQ(MsgPackReadExtension(*MsgPack, #Null, 2), #MsgPack_InvalidSize, "Reading into a #Null buffer fails")
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_NullPointerGiven, "Null output buffer error is set")
AssertEquals(*MsgPack\BufferOffset, 0, "Offset is untouched by the failed read")
FreeWrappedHex(*MsgPack)
; Ending
FreeMemory(*Source)
FreeMemory(*Output)
If FailedUnitTests
End 1
EndIf
Debug "Done !"
End 0
@@ -0,0 +1,255 @@
EnableExplicit
XIncludeFile "../_UnitTesting.pbi"
XIncludeFile "../../Includes/MessagePack.pbi"
XIncludeFile "../_TestBuffers.pbi"
; Setup
; Starting with 1 byte so every write has to grow the buffer.
Define *MsgPack.MsgPackData = MsgPackCreate(1)
Define *Source = AllocatePattern(65536)
Define *Output = AllocateMemory(65536)
Define *Copy
Define OffsetEmpty.i, OffsetFix1.i, OffsetFix2.i, OffsetFix4.i, OffsetFix8.i, OffsetFix16.i
Define Offset3.i, Offset255.i, Offset256.i, Offset65535.i, Offset65536.i
Define OffsetExt8Fix.i, OffsetExt16Small.i, OffsetExt32Empty.i, OffsetFixExt4.i, OffsetFixExt16.i
Define OffsetEnd.i
; Automatic format code selection, on each boundary
OffsetEmpty = *MsgPack\BufferOffset
AssertEquals(MsgPackWriteExtension(*MsgPack, 5, #Null, 0), #MsgPack_Error_Success, "Wrote 0 bytes from a #Null buffer")
OffsetFix1 = *MsgPack\BufferOffset
AssertEquals(MsgPackWriteExtension(*MsgPack, 1, *Source, 1), #MsgPack_Error_Success, "Wrote 1 byte")
OffsetFix2 = *MsgPack\BufferOffset
AssertEquals(MsgPackWriteExtension(*MsgPack, -1, *Source, 2), #MsgPack_Error_Success, "Wrote 2 bytes")
OffsetFix4 = *MsgPack\BufferOffset
AssertEquals(MsgPackWriteExtension(*MsgPack, 127, *Source, 4), #MsgPack_Error_Success, "Wrote 4 bytes")
OffsetFix8 = *MsgPack\BufferOffset
AssertEquals(MsgPackWriteExtension(*MsgPack, -128, *Source, 8), #MsgPack_Error_Success, "Wrote 8 bytes")
OffsetFix16 = *MsgPack\BufferOffset
AssertEquals(MsgPackWriteExtension(*MsgPack, 42, *Source, 16), #MsgPack_Error_Success, "Wrote 16 bytes")
Offset3 = *MsgPack\BufferOffset
AssertEquals(MsgPackWriteExtension(*MsgPack, 3, *Source, 3), #MsgPack_Error_Success, "Wrote 3 bytes")
Offset255 = *MsgPack\BufferOffset
AssertEquals(MsgPackWriteExtension(*MsgPack, 6, *Source, 255), #MsgPack_Error_Success, "Wrote 255 bytes")
Offset256 = *MsgPack\BufferOffset
AssertEquals(MsgPackWriteExtension(*MsgPack, 7, *Source, 256), #MsgPack_Error_Success, "Wrote 256 bytes")
Offset65535 = *MsgPack\BufferOffset
AssertEquals(MsgPackWriteExtension(*MsgPack, 8, *Source, 65535), #MsgPack_Error_Success, "Wrote 65535 bytes")
Offset65536 = *MsgPack\BufferOffset
AssertEquals(MsgPackWriteExtension(*MsgPack, 9, *Source, 65536), #MsgPack_Error_Success, "Wrote 65536 bytes")
; Forced format codes, even when a smaller one fits
OffsetExt8Fix = *MsgPack\BufferOffset
AssertEquals(MsgPackWriteExt8(*MsgPack, 10, *Source, 4), #MsgPack_Error_Success, "Wrote 4 bytes as ext8")
OffsetExt16Small = *MsgPack\BufferOffset
AssertEquals(MsgPackWriteExt16(*MsgPack, 11, *Source, 1), #MsgPack_Error_Success, "Wrote 1 byte as ext16")
OffsetExt32Empty = *MsgPack\BufferOffset
AssertEquals(MsgPackWriteExt32(*MsgPack, 12, #Null, 0), #MsgPack_Error_Success, "Wrote 0 bytes as ext32")
OffsetFixExt4 = *MsgPack\BufferOffset
AssertEquals(MsgPackWriteFixExt4(*MsgPack, 13, *Source), #MsgPack_Error_Success, "Wrote fixext4")
OffsetFixExt16 = *MsgPack\BufferOffset
AssertEquals(MsgPackWriteFixExt16(*MsgPack, 14, *Source), #MsgPack_Error_Success, "Wrote fixext16")
; Something right after, to catch offset drifts
AssertEquals(MsgPackWriteUInt8(*MsgPack, 42), #MsgPack_Error_Success, "Wrote trailing UInt8")
OffsetEnd = *MsgPack\BufferOffset
; Offsets
AssertEquals(OffsetFix1, OffsetEmpty + 3, "Empty ext8 takes 3 bytes")
AssertEquals(OffsetFix2, OffsetFix1 + 2 + 1, "fixext1 takes 3 bytes")
AssertEquals(OffsetFix4, OffsetFix2 + 2 + 2, "fixext2 takes 4 bytes")
AssertEquals(OffsetFix8, OffsetFix4 + 2 + 4, "fixext4 takes 6 bytes")
AssertEquals(OffsetFix16, OffsetFix8 + 2 + 8, "fixext8 takes 10 bytes")
AssertEquals(Offset3, OffsetFix16 + 2 + 16, "fixext16 takes 18 bytes")
AssertEquals(Offset255, Offset3 + 3 + 3, "3 bytes ext8 takes 6 bytes")
AssertEquals(Offset256, Offset255 + 3 + 255, "255 bytes ext8 takes 258 bytes")
AssertEquals(Offset65535, Offset256 + 4 + 256, "256 bytes ext16 takes 260 bytes")
AssertEquals(Offset65536, Offset65535 + 4 + 65535, "65535 bytes ext16 takes 65539 bytes")
AssertEquals(OffsetExt8Fix, Offset65536 + 6 + 65536, "65536 bytes ext32 takes 65542 bytes")
AssertEquals(OffsetExt16Small, OffsetExt8Fix + 3 + 4, "4 bytes ext8 takes 7 bytes")
AssertEquals(OffsetExt32Empty, OffsetExt16Small + 4 + 1, "1 byte ext16 takes 5 bytes")
AssertEquals(OffsetFixExt4, OffsetExt32Empty + 6, "Empty ext32 takes 6 bytes")
AssertEquals(OffsetFixExt16, OffsetFixExt4 + 2 + 4, "Forced fixext4 takes 6 bytes")
AssertEquals(OffsetEnd, OffsetFixExt16 + 2 + 16 + 2, "Forced fixext16 and UInt8 take 20 bytes")
AssertEquals(*MsgPack\BufferSize, OffsetEnd, "Buffer grew to exactly what was written")
; Big-endian layouts
AssertEqualsA(PeekA(*MsgPack\Buffer + OffsetEmpty), #MsgPack_FormatCode_Ext8, "Empty uses ext8")
AssertEqualsA(PeekA(*MsgPack\Buffer + OffsetEmpty + 1), $00, "Empty ext8 size")
AssertEqualsB(PeekB(*MsgPack\Buffer + OffsetEmpty + 2), 5, "Empty ext8 type")
AssertEqualsA(PeekA(*MsgPack\Buffer + OffsetFix1), #MsgPack_FormatCode_FixExt1, "1 byte uses fixext1")
AssertEqualsB(PeekB(*MsgPack\Buffer + OffsetFix1 + 1), 1, "fixext1 type")
AssertEqualsA(PeekA(*MsgPack\Buffer + OffsetFix1 + 2), PeekA(*Source), "fixext1 data")
AssertEqualsA(PeekA(*MsgPack\Buffer + OffsetFix2), #MsgPack_FormatCode_FixExt2, "2 bytes uses fixext2")
AssertEqualsA(PeekA(*MsgPack\Buffer + OffsetFix2 + 1), $FF, "fixext2 negative type")
Assert(CompareMemory(*MsgPack\Buffer + OffsetFix2 + 2, *Source, 2), "fixext2 data")
AssertEqualsA(PeekA(*MsgPack\Buffer + OffsetFix4), #MsgPack_FormatCode_FixExt4, "4 bytes uses fixext4")
AssertEqualsA(PeekA(*MsgPack\Buffer + OffsetFix4 + 1), $7F, "fixext4 type")
AssertEqualsA(PeekA(*MsgPack\Buffer + OffsetFix8), #MsgPack_FormatCode_FixExt8, "8 bytes uses fixext8")
AssertEqualsA(PeekA(*MsgPack\Buffer + OffsetFix8 + 1), $80, "fixext8 type")
AssertEqualsA(PeekA(*MsgPack\Buffer + OffsetFix16), #MsgPack_FormatCode_FixExt16, "16 bytes uses fixext16")
Assert(CompareMemory(*MsgPack\Buffer + OffsetFix16 + 2, *Source, 16), "fixext16 data")
AssertEqualsA(PeekA(*MsgPack\Buffer + Offset3), #MsgPack_FormatCode_Ext8, "3 bytes uses ext8")
AssertEqualsA(PeekA(*MsgPack\Buffer + Offset3 + 1), $03, "3 bytes ext8 size")
AssertEqualsB(PeekB(*MsgPack\Buffer + Offset3 + 2), 3, "3 bytes ext8 type")
Assert(CompareMemory(*MsgPack\Buffer + Offset3 + 3, *Source, 3), "3 bytes ext8 data")
AssertEqualsA(PeekA(*MsgPack\Buffer + Offset255), #MsgPack_FormatCode_Ext8, "255 bytes uses ext8")
AssertEqualsA(PeekA(*MsgPack\Buffer + Offset255 + 1), $FF, "255 bytes ext8 size")
AssertEqualsA(PeekA(*MsgPack\Buffer + Offset256), #MsgPack_FormatCode_Ext16, "256 bytes uses ext16")
AssertEqualsA(PeekA(*MsgPack\Buffer + Offset256 + 1), $01, "256 bytes ext16 size high byte")
AssertEqualsA(PeekA(*MsgPack\Buffer + Offset256 + 2), $00, "256 bytes ext16 size low byte")
AssertEqualsB(PeekB(*MsgPack\Buffer + Offset256 + 3), 7, "256 bytes ext16 type")
AssertEqualsA(PeekA(*MsgPack\Buffer + Offset65535), #MsgPack_FormatCode_Ext16, "65535 bytes uses ext16")
AssertEqualsA(PeekA(*MsgPack\Buffer + Offset65535 + 1), $FF, "65535 bytes ext16 size high byte")
AssertEqualsA(PeekA(*MsgPack\Buffer + Offset65535 + 2), $FF, "65535 bytes ext16 size low byte")
AssertEqualsA(PeekA(*MsgPack\Buffer + Offset65536), #MsgPack_FormatCode_Ext32, "65536 bytes uses ext32")
AssertEqualsA(PeekA(*MsgPack\Buffer + Offset65536 + 1), $00, "65536 bytes ext32 size byte 1")
AssertEqualsA(PeekA(*MsgPack\Buffer + Offset65536 + 2), $01, "65536 bytes ext32 size byte 2")
AssertEqualsA(PeekA(*MsgPack\Buffer + Offset65536 + 3), $00, "65536 bytes ext32 size byte 3")
AssertEqualsA(PeekA(*MsgPack\Buffer + Offset65536 + 4), $00, "65536 bytes ext32 size byte 4")
AssertEqualsB(PeekB(*MsgPack\Buffer + Offset65536 + 5), 9, "65536 bytes ext32 type")
Assert(CompareMemory(*MsgPack\Buffer + Offset65536 + 6, *Source, 65536), "65536 bytes ext32 data")
AssertEqualsA(PeekA(*MsgPack\Buffer + OffsetExt8Fix), #MsgPack_FormatCode_Ext8, "Forced 4 bytes ext8")
AssertEqualsA(PeekA(*MsgPack\Buffer + OffsetExt8Fix + 1), $04, "Forced 4 bytes ext8 size")
AssertEqualsB(PeekB(*MsgPack\Buffer + OffsetExt8Fix + 2), 10, "Forced 4 bytes ext8 type")
AssertEqualsA(PeekA(*MsgPack\Buffer + OffsetExt32Empty), #MsgPack_FormatCode_Ext32, "Forced empty ext32")
AssertEqualsA(PeekA(*MsgPack\Buffer + OffsetExt32Empty + 4), $00, "Forced empty ext32 size byte 4")
AssertEqualsB(PeekB(*MsgPack\Buffer + OffsetExt32Empty + 5), 12, "Forced empty ext32 type")
AssertEqualsA(PeekA(*MsgPack\Buffer + OffsetFixExt4), #MsgPack_FormatCode_FixExt4, "Forced fixext4")
AssertEqualsA(PeekA(*MsgPack\Buffer + OffsetFixExt16), #MsgPack_FormatCode_FixExt16, "Forced fixext16")
AssertEqualsB(PeekB(*MsgPack\Buffer + OffsetFixExt16 + 1), 14, "Forced fixext16 type")
; Reading them back
*MsgPack\BufferOffset = 0
AssertEqualsB(MsgPackGetExtensionType(*MsgPack), 5, "Empty ext8 type is read")
AssertEquals(*MsgPack\BufferOffset, 0, "Reading the type didn't move the offset")
AssertEqualsQ(MsgPackReadExtension(*MsgPack, #Null, 0), 0, "Read empty ext8 into a #Null buffer")
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_Success, "Empty read is a success")
AssertEqualsB(MsgPackGetExtensionType(*MsgPack), 1, "fixext1 type is read")
FillMemory(*Output, 65536, $AA)
AssertEqualsQ(MsgPackReadExtension(*MsgPack, *Output, 1), 1, "Read fixext1 into an exactly sized buffer")
AssertEqualsA(PeekA(*Output), PeekA(*Source), "fixext1 data matches")
AssertEqualsA(PeekA(*Output + 1), $AA, "Nothing written past the data")
AssertEqualsB(MsgPackGetExtensionType(*MsgPack), -1, "fixext2 negative type is read")
AssertEqualsQ(MsgPackReadExtension(*MsgPack, *Output, 65536), 2, "Read fixext2")
Assert(CompareMemory(*Output, *Source, 2), "fixext2 data matches")
AssertEqualsB(MsgPackGetExtensionType(*MsgPack), 127, "fixext4 type is read")
AssertEqualsQ(MsgPackReadExtension(*MsgPack, *Output, 65536), 4, "Read fixext4")
Assert(CompareMemory(*Output, *Source, 4), "fixext4 data matches")
AssertEqualsB(MsgPackGetExtensionType(*MsgPack), -128, "fixext8 type is read")
*Copy = MsgPackCopyExtension(*MsgPack)
Assert(Bool(*Copy <> #Null), "Copied fixext8")
If *Copy
AssertEquals(MemorySize(*Copy), 8, "Copy is exactly 8 bytes")
Assert(CompareMemory(*Copy, *Source, 8), "fixext8 data matches")
FreeMemory(*Copy)
EndIf
AssertEqualsB(MsgPackGetExtensionType(*MsgPack), 42, "fixext16 type is read")
AssertEqualsQ(MsgPackReadExtension(*MsgPack, *Output, 16), 16, "Read fixext16")
Assert(CompareMemory(*Output, *Source, 16), "fixext16 data matches")
AssertEqualsB(MsgPackGetExtensionType(*MsgPack), 3, "3 bytes ext8 type is read")
AssertEqualsQ(MsgPackReadExtension(*MsgPack, *Output, 65536), 3, "Read 3 bytes ext8")
Assert(CompareMemory(*Output, *Source, 3), "3 bytes ext8 data matches")
AssertEqualsQ(MsgPackReadExtension(*MsgPack, *Output, 255), 255, "Read 255 bytes ext8")
Assert(CompareMemory(*Output, *Source, 255), "255 bytes data matches")
AssertEqualsB(MsgPackGetExtensionType(*MsgPack), 7, "256 bytes ext16 type is read")
*Copy = MsgPackCopyExtension(*MsgPack)
Assert(Bool(*Copy <> #Null), "Copied 256 bytes")
If *Copy
AssertEquals(MemorySize(*Copy), 256, "Copy is exactly 256 bytes")
Assert(CompareMemory(*Copy, *Source, 256), "256 bytes data matches")
FreeMemory(*Copy)
EndIf
AssertEqualsQ(MsgPackReadExtension(*MsgPack, *Output, 65536), 65535, "Read 65535 bytes ext16")
Assert(CompareMemory(*Output, *Source, 65535), "65535 bytes data matches")
AssertEqualsB(MsgPackGetExtensionType(*MsgPack), 9, "65536 bytes ext32 type is read")
*Copy = MsgPackCopyExtension(*MsgPack)
Assert(Bool(*Copy <> #Null), "Copied 65536 bytes")
If *Copy
AssertEquals(MemorySize(*Copy), 65536, "Copy is exactly 65536 bytes")
Assert(CompareMemory(*Copy, *Source, 65536), "65536 bytes data matches")
FreeMemory(*Copy)
EndIf
AssertEqualsB(MsgPackGetExtensionType(*MsgPack), 10, "Forced ext8 type is read")
AssertEqualsQ(MsgPackReadExtension(*MsgPack, *Output, 65536), 4, "Read forced 4 bytes ext8")
Assert(CompareMemory(*Output, *Source, 4), "Forced 4 bytes ext8 data matches")
AssertEqualsB(MsgPackGetExtensionType(*MsgPack), 11, "Forced ext16 type is read")
AssertEqualsQ(MsgPackReadExtension(*MsgPack, *Output, 65536), 1, "Read forced 1 byte ext16")
AssertEqualsA(PeekA(*Output), PeekA(*Source), "Forced 1 byte ext16 data matches")
AssertEqualsB(MsgPackGetExtensionType(*MsgPack), 12, "Forced ext32 type is read")
AssertEquals(MsgPackCopyExtension(*MsgPack), #Null, "Copying an empty ext32 returns #Null")
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_Success, "Empty copy is a success")
AssertEquals(*MsgPack\BufferOffset, OffsetFixExt4, "Empty copy still moved the offset")
AssertEqualsB(MsgPackGetExtensionType(*MsgPack), 13, "Forced fixext4 type is read")
AssertEqualsQ(MsgPackReadExtension(*MsgPack, *Output, 65536), 4, "Read forced fixext4")
Assert(CompareMemory(*Output, *Source, 4), "Forced fixext4 data matches")
AssertEqualsB(MsgPackGetExtensionType(*MsgPack), 14, "Forced fixext16 type is read")
AssertEqualsQ(MsgPackReadExtension(*MsgPack, *Output, 65536), 16, "Read forced fixext16")
Assert(CompareMemory(*Output, *Source, 16), "Forced fixext16 data matches")
AssertEqualsA(MsgPackReadUInt8(*MsgPack), 42, "Read trailing UInt8")
AssertEquals(*MsgPack\BufferOffset, OffsetEnd, "Offset is at the end")
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_Success, "No error after reading")
MsgPackFree(*MsgPack, #True)
; Known vector: timestamp 32 for 1970-01-01T00:00:01Z
*MsgPack = WrapHex("D6FF00000001")
AssertEquals(MsgPackFormatCodeToDataType(MsgPackGetFormatCode(*MsgPack)), #MsgPack_DataTypes_Extension, "Timestamp is detected as an extension")
AssertEqualsB(MsgPackGetExtensionType(*MsgPack), -1, "Timestamp type is -1")
AssertEqualsQ(MsgPackReadExtension(*MsgPack, *Output, 4), 4, "Read the timestamp's 4 bytes")
AssertEquals(EndianSwapI32(PeekL(*Output)), 1, "Timestamp seconds are big-endian")
AssertEquals(*MsgPack\BufferOffset, 6, "Offset is at the end")
FreeWrappedHex(*MsgPack)
; Ending
FreeMemory(*Source)
FreeMemory(*Output)
If FailedUnitTests
End 1
EndIf
Debug "Done !"
End 0