mirror of
https://github.com/aziascreations/PB-MessagePack.git
synced 2026-11-03 17:01:24 +01:00
Implemented Iterators, Arrays and Maps
Ready for test integration, Used stuff setup a while back Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -10,7 +10,10 @@ PureBasic include for creating/reading MessagePack data.
|
||||
* ???
|
||||
* The internal offset is auto-incremented on all complete reads and writes.
|
||||
* Does not apply to `MsgPackGetFormatCode`.
|
||||
* Can be disabled with the internal `#MsgPack_DataFlags_MoveOffsetAfterRead/Write` flags, honored by every read and write.
|
||||
* The internal buffer is automatically resized as needed.
|
||||
* Invalid or truncated data never raises a `DebuggerError`, only programming errors do. (e.g.: `#Null` pointers)
|
||||
* Views from `MsgPackExamine` are read-only, writes return `#MsgPack_Error_BufferIsReadOnly`.
|
||||
|
||||
|
||||
### Type aliases/macros
|
||||
@@ -86,7 +89,10 @@ PureBasic include for creating/reading MessagePack data.
|
||||
* `#MsgPack_Error_OutputBufferTooSmall`
|
||||
* `#MsgPack_Error_UnsupportedStringEncoding`
|
||||
* `#MsgPack_Error_InvalidStringSize`
|
||||
* `#MsgPack_Error_TooManyEntries`
|
||||
* `#MsgPack_Error_OddMapFieldCount`
|
||||
* `#MsgPack_Error_CannotGrowBufferDueToConfig`
|
||||
* `#MsgPack_Error_BufferIsReadOnly`
|
||||
|
||||
* `.MsgPack_DataFlag` \
|
||||
Internal flags, do not touch.
|
||||
@@ -137,6 +143,14 @@ PureBasic include for creating/reading MessagePack data.
|
||||
Attempts to write `#False` at the current internal offset. \
|
||||
Returns `#MsgPack_Error_Success` on success, something else otherwise.
|
||||
|
||||
* `MsgPackWriteBoolean.MsgPack_ErrorCode(*MsgPackData.MsgPackData, Boolean.b)`
|
||||
Writes `#True` or `#False` depending on `Boolean`. \
|
||||
Returns `#MsgPack_Error_Success` on success, something else otherwise.
|
||||
|
||||
* `MsgPackReadNull.MsgPack_ErrorCode(*MsgPackData.MsgPackData)`
|
||||
Attempts to read a nil at the current internal offset. \
|
||||
Returns `#MsgPack_Error_Success` on success, something else otherwise, in which case the offset isn't moved.
|
||||
|
||||
|
||||
### Integers & Floats
|
||||
* `MsgPackWriteInteger.MsgPack_ErrorCode(*MsgPackData.MsgPackData, Value.q)` \
|
||||
@@ -226,6 +240,81 @@ Size errors reuse the `#MsgPack_Error_BinaryDataSize*` codes.
|
||||
Same as `MsgPackCopyBinary`, for the data of ext fields.
|
||||
|
||||
|
||||
### Navigation
|
||||
Arrays and maps are handled iteratively, so deep nesting can't overflow the stack.
|
||||
|
||||
* `MsgPackGetFieldFullSize.q(*MsgPackData.MsgPackData)` \
|
||||
Returns the full size in bytes of the field at the current internal offset, without moving it. \
|
||||
Arrays and maps include all of their contained fields, recursively. \
|
||||
Returns `#MsgPack_InvalidSize` on errors and sets the internal error code. \
|
||||
See: `MsgPackLastError`
|
||||
|
||||
* `MsgPackGetEntryCount.q(*MsgPackData.MsgPackData)` \
|
||||
Returns the number of entries in the array or map at the current internal offset, without moving it. \
|
||||
A map entry is a key and a value, and counts as one. \
|
||||
Returns `#MsgPack_InvalidSize` on errors and sets the internal error code.
|
||||
|
||||
* `MsgPackSkip.MsgPack_ErrorCode(*MsgPackData.MsgPackData, FieldCount.q = 1)` \
|
||||
Moves the internal offset past the next `FieldCount` fields, the ones contained in arrays and maps included. \
|
||||
Map keys and values count as separate fields. \
|
||||
Returns `#MsgPack_Error_Success` on success, something else otherwise, in which case the offset isn't moved.
|
||||
|
||||
|
||||
### Arrays & Maps
|
||||
* `MsgPackExamine.i(*MsgPackData.MsgPackData, ExaminationOptions.MsgPack_ExaminationOption = 0)` \
|
||||
Returns a view over the array or map at the current internal offset, to iterate on with `MsgPackNextEntry`. \
|
||||
The view is a regular `MsgPackData` limited to the array/map's content, so all the regular read functions work on it, and can't read past it. \
|
||||
It reads straight from `*MsgPackData`'s buffer, which must not be grown or freed while the view is used. \
|
||||
The whole array/map is checked beforehand, and the offset of `*MsgPackData` is moved past it. \
|
||||
Returns `#Null` on errors and sets the internal error code. \
|
||||
See: `MsgPackLastError`
|
||||
* `#MsgPack_ExaminationOptions_Unsafe` - **Not recommended !** \
|
||||
Skips the content check, which is also what finds the array/map's end. \
|
||||
The view can read past the array/map until `MsgPackNextEntry` reaches its end, invalid content is only
|
||||
detected while iterating, and the offset of `*MsgPackData` isn't moved, use `MsgPackSkip` afterward.
|
||||
|
||||
* `MsgPackNextEntry.b(*Iterator.MsgPackData)` \
|
||||
Moves the view's offset to its next entry, the first one on the first call. \
|
||||
Whatever wasn't read from the current entry is skipped, nested arrays/maps included. \
|
||||
Map entries are read key first, then value. \
|
||||
Returns `#True` if an entry is available, `#False` at the end or on errors, with the internal error code set accordingly.
|
||||
|
||||
* `MsgPackFinish(*Iterator.MsgPackData)` \
|
||||
Frees a view from `MsgPackExamine`, without touching the underlying buffer.
|
||||
|
||||
```purebasic
|
||||
Define *Iterator.MsgPackData = MsgPackExamine(*MsgPack)
|
||||
If *Iterator
|
||||
While MsgPackNextEntry(*Iterator)
|
||||
Key.s = MsgPackReadString(*Iterator)
|
||||
If MsgPackFormatCodeToDataType(MsgPackGetFormatCode(*Iterator)) = #MsgPack_DataTypes_Map
|
||||
*Nested = MsgPackExamine(*Iterator) ; Or leave it, it'll be skipped
|
||||
; ...
|
||||
MsgPackFinish(*Nested)
|
||||
EndIf
|
||||
Wend
|
||||
MsgPackFinish(*Iterator)
|
||||
EndIf
|
||||
```
|
||||
|
||||
* `MsgPackWriteArray.MsgPack_ErrorCode(*MsgPackData.MsgPackData, *Buffer, BufferSize.q)` \
|
||||
Writes an array whose entries are the fields in the `BufferSize` bytes of MessagePack data at `*Buffer`, using the smallest array format code that fits. \
|
||||
The data is fully checked and its fields are counted, nested arrays/maps counting as one. \
|
||||
`*Buffer` can come from `MsgPackFinalize`, with `MemorySize()` as its size, but not from `*MsgPackData`'s own buffer. \
|
||||
Returns `#MsgPack_Error_Success` on success, something else otherwise, with nothing written.
|
||||
|
||||
* `MsgPackWriteMap.MsgPack_ErrorCode(*MsgPackData.MsgPackData, *Buffer, BufferSize.q)` \
|
||||
Same as `MsgPackWriteArray`, for maps. \
|
||||
The data must hold an even number of fields, alternating keys and values, or `#MsgPack_Error_OddMapFieldCount` is returned.
|
||||
|
||||
* `MsgPackWriteArrayFrom/MapFrom.MsgPack_ErrorCode(*MsgPackData.MsgPackData, *Source.MsgPackData)` \
|
||||
Same as `MsgPackWriteArray/Map`, with the data written so far in `*Source`, which is left untouched.
|
||||
|
||||
* `MsgPackWriteFixArray/Array16/Array32/FixMap/Map16/Map32.MsgPack_ErrorCode(*MsgPackData.MsgPackData, *Buffer, BufferSize.q)` \
|
||||
Same as `MsgPackWriteArray/Map`, but forces the given format code. \
|
||||
Returns `#MsgPack_Error_TooManyEntries` if the entries don't fit in it.
|
||||
|
||||
|
||||
## Remarks
|
||||
* Never touch the `MsgPackData` structure unless you **absolutely** have to.
|
||||
|
||||
|
||||
@@ -151,8 +151,7 @@ EndProcedure
|
||||
|
||||
|
||||
; TODO: Add callback in the structure, maybe ?
|
||||
; FIXME: Use a .q for the size !!!!
|
||||
Procedure.MsgPack_ErrorCode MsgPackGrow(*MsgPackData.MsgPackData, DesiredSizeIncrease.i)
|
||||
Procedure.MsgPack_ErrorCode MsgPackGrow(*MsgPackData.MsgPackData, DesiredSizeIncrease.q)
|
||||
Protected NewSize.q
|
||||
Protected *NewBuffer
|
||||
|
||||
|
||||
@@ -140,6 +140,7 @@ Procedure.MsgPack_ErrorCode _MsgPackWriteSizedField(*MsgPackData.MsgPackData, Fo
|
||||
CopyMemory(*Data, *MsgPackData\Buffer + *MsgPackData\BufferOffset, DataSize)
|
||||
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + DataSize
|
||||
EndIf
|
||||
_MsgPackRewindOffsetAfterWrite(*MsgPackData, 1 + SizeFieldByteLength + DataSize)
|
||||
|
||||
ProcedureReturn #MsgPack_Error_Success
|
||||
EndProcedure
|
||||
@@ -273,8 +274,8 @@ Procedure.q _MsgPackReadSizedFieldData(*MsgPackData.MsgPackData, ExpectedDataTyp
|
||||
CopyMemory(*MsgPackData\Buffer + *MsgPackData\BufferOffset + HeaderSize, *OutputBuffer, DataSize)
|
||||
EndIf
|
||||
|
||||
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + HeaderSize + DataSize
|
||||
|
||||
_MsgPackMoveOffsetAfterRead(*MsgPackData, HeaderSize + DataSize)
|
||||
|
||||
ProcedureReturn DataSize
|
||||
EndProcedure
|
||||
|
||||
@@ -304,8 +305,8 @@ Procedure.i _MsgPackCopySizedFieldData(*MsgPackData.MsgPackData, ExpectedDataTyp
|
||||
CopyMemory(*MsgPackData\Buffer + *MsgPackData\BufferOffset + HeaderSize, *ReturnedValue, DataSize)
|
||||
EndIf
|
||||
|
||||
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + HeaderSize + DataSize
|
||||
|
||||
_MsgPackMoveOffsetAfterRead(*MsgPackData, HeaderSize + DataSize)
|
||||
|
||||
ProcedureReturn *ReturnedValue
|
||||
EndProcedure
|
||||
|
||||
|
||||
+44
-10
@@ -29,9 +29,10 @@ CompilerIf #PB_Compiler_IsMainFile
|
||||
EnableExplicit
|
||||
CompilerEndIf
|
||||
|
||||
; FIXME: Implement something to not load the include twice in other programs
|
||||
#_MsgPack_EndiannessIncludePath = "./PB-Endianness/Includes/Endianness.pbi"
|
||||
XIncludeFile #_MsgPack_EndiannessIncludePath
|
||||
; Skipped if "Endianness.pbi" was already included from another path, by the program or another include.
|
||||
CompilerIf Not Defined(_NibblePoker_Endianness_WasIncluded, #PB_Constant) And Not Defined(EndianSwapQ, #PB_Procedure)
|
||||
XIncludeFile "./PB-Endianness/Includes/Endianness.pbi"
|
||||
CompilerEndIf
|
||||
|
||||
|
||||
|
||||
@@ -163,12 +164,18 @@ Enumeration _MsgPack_ErrorCodes
|
||||
#MsgPack_Error_UnsupportedStringEncoding = 60
|
||||
#MsgPack_Error_InvalidStringSize = 61
|
||||
|
||||
; 7x - Arrays & Maps
|
||||
#MsgPack_Error_TooManyEntries = 70
|
||||
#MsgPack_Error_OddMapFieldCount = 71
|
||||
|
||||
|
||||
#MsgPack_Error_CannotGrowBufferDueToConfig = 100
|
||||
#MsgPack_Error_BufferIsReadOnly = 101
|
||||
EndEnumeration
|
||||
|
||||
EnumerationBinary _MsgPack_DataFlags
|
||||
; FIXME: Not implemented
|
||||
; Prevents any write in a given `MsgPackData`, set on views from `MsgPackExamine()`.
|
||||
; Writes return `#MsgPack_Error_BufferIsReadOnly`.
|
||||
#MsgPack_DataFlags_IsReadOnly = %0000000000000001
|
||||
|
||||
; Prevents a `MsgPackData` structure from auto-growing its buffer when needed.
|
||||
@@ -186,22 +193,29 @@ EnumerationBinary _MsgPack_DataFlags
|
||||
; writing a value using the standard procedures.
|
||||
#MsgPack_DataFlags_MoveOffsetAfterWrite = %0000000000010000
|
||||
|
||||
; FIXME: Not implemented
|
||||
; Indicates a given `MsgPackData` is made to be iterated on.
|
||||
; Indicates a given `MsgPackData` is a view made to be iterated on with `MsgPackNextEntry()`.
|
||||
; And it implies it represents a Map or Array.
|
||||
#MsgPack_DataFlags_Iterator = %0000000000100000
|
||||
; Set on map views, whose entries are made of a key and a value.
|
||||
#MsgPack_DataFlags_CanProvideKeys = %0000000001000000
|
||||
; Set on both map and array views.
|
||||
#MsgPack_DataFlags_CanProvideVals = %0000000010000000
|
||||
EndEnumeration
|
||||
|
||||
; Flags set on every new `MsgPackData`, so all reads and writes move the offset.
|
||||
#_MsgPack_DefaultDataFlags = #MsgPack_DataFlags_MoveOffsetAfterRead | #MsgPack_DataFlags_MoveOffsetAfterWrite
|
||||
|
||||
; Flags set on every view from `MsgPackExamine()`, which never own nor grow their buffer.
|
||||
EnumerationBinary _MsgPack_ExaminationOptions
|
||||
; If set, we don't seek the end of the examined map/array.
|
||||
#MsgPack_ExaminationOptions_NoEndSeeking = %0000000000000001
|
||||
; Not recommended !
|
||||
; Skips the check of the examined array or map's content, which also finds its end.
|
||||
; The view can then read past the array or map until `MsgPackNextEntry()` reaches its end,
|
||||
; invalid content is only detected while iterating, and the examined offset isn't moved.
|
||||
#MsgPack_ExaminationOptions_Unsafe = %0000000000000001
|
||||
EndEnumeration
|
||||
|
||||
#_MsgPack_ViewDataFlags =#_MsgPack_DefaultDataFlags | #MsgPack_DataFlags_IsReadOnly | #MsgPack_DataFlags_CannotGrow | #MsgPack_DataFlags_NoBufferFree | #MsgPack_DataFlags_Iterator | #MsgPack_DataFlags_CanProvideVals
|
||||
|
||||
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
@@ -235,8 +249,13 @@ Structure MsgPackData
|
||||
; If set to `1` or lower, we'll ignore this mechanism.
|
||||
BufferGrowthIncrement.i
|
||||
|
||||
; IteratorSizeLeft, or snoething like this.
|
||||
|
||||
; Offset of the iterator's current entry, only used by views from `MsgPackExamine()`.
|
||||
; Set to `#MsgPack_InvalidSize` until the first `MsgPackNextEntry()` call.
|
||||
IteratorEntryOffset.i
|
||||
|
||||
; Number of fields left to iterate on, map keys and values included, only used by views.
|
||||
IteratorFieldsLeft.q
|
||||
|
||||
; LastError code
|
||||
LastError.MsgPack_ErrorCode
|
||||
|
||||
@@ -265,6 +284,21 @@ Macro _MsgPackHasFlag(MsgPackWrapper, DataFlag)
|
||||
((MsgPackWrapper\DataFlags & DataFlag) <> $0000)
|
||||
EndMacro
|
||||
|
||||
; Moves the offset past a field of `Size` bytes that was fully read, if the flags allow it.
|
||||
Macro _MsgPackMoveOffsetAfterRead(MsgPackWrapper, Size)
|
||||
If _MsgPackHasFlag(MsgPackWrapper, #MsgPack_DataFlags_MoveOffsetAfterRead)
|
||||
MsgPackWrapper\BufferOffset = MsgPackWrapper\BufferOffset + (Size)
|
||||
EndIf
|
||||
EndMacro
|
||||
|
||||
; Moves the offset back before a field of `Size` bytes that was fully written, if the flags don't allow it to move.
|
||||
; Writes always move the offset as they go, since most of them are done in multiple steps.
|
||||
Macro _MsgPackRewindOffsetAfterWrite(MsgPackWrapper, Size)
|
||||
If Not _MsgPackHasFlag(MsgPackWrapper, #MsgPack_DataFlags_MoveOffsetAfterWrite)
|
||||
MsgPackWrapper\BufferOffset = MsgPackWrapper\BufferOffset - (Size)
|
||||
EndIf
|
||||
EndMacro
|
||||
|
||||
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
|
||||
@@ -105,6 +105,8 @@ Procedure.MsgPack_ErrorCode _MsgPackWriteExtField(*MsgPackData.MsgPackData, Form
|
||||
CopyMemory(*Data, *MsgPackData\Buffer + *MsgPackData\BufferOffset, DataSize)
|
||||
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + DataSize
|
||||
EndIf
|
||||
; Format code, ext type, and size field for non-fixext ones.
|
||||
_MsgPackRewindOffsetAfterWrite(*MsgPackData, 2 + SizeFieldByteLength + DataSize)
|
||||
|
||||
ProcedureReturn #MsgPack_Error_Success
|
||||
EndProcedure
|
||||
|
||||
+558
-127
@@ -26,6 +26,7 @@ CompilerEndIf
|
||||
XIncludeFile "./_Commons.pbi"
|
||||
XIncludeFile "./_Utils.pbi"
|
||||
XIncludeFile "./_Allocators.pbi"
|
||||
XIncludeFile "./_Binary.pbi"
|
||||
|
||||
|
||||
|
||||
@@ -52,7 +53,7 @@ Macro _MsgPackGetFieldDynamicSize_Within(MsgPackData, Mask) : (PeekA(MsgPackData
|
||||
; Remark: Doesn't apply to Int/UInt variants since those have their own function for extracting the value.
|
||||
Procedure.q _MsgPackGetFieldDynamicSize_1After(*MsgPackData.MsgPackData)
|
||||
If Not _MsgPackHasSpaceLeft(*MsgPackData, 1 + 1)
|
||||
DebuggerWarning("We have less than 1 byte to read the field's dynamic size !")
|
||||
Debug("We have less than 1 byte to read the field's dynamic size !")
|
||||
ProcedureReturn #MsgPack_InvalidSize
|
||||
EndIf
|
||||
|
||||
@@ -64,7 +65,7 @@ EndProcedure
|
||||
; Remark: Doesn't apply to Int/UInt variants since those have their own function for extracting the value.
|
||||
Procedure.q _MsgPackGetFieldDynamicSize_2After(*MsgPackData.MsgPackData)
|
||||
If Not _MsgPackHasSpaceLeft(*MsgPackData, 1 + 2)
|
||||
DebuggerWarning("We have less than 2 bytes to read the field's dynamic size !")
|
||||
Debug("We have less than 2 bytes to read the field's dynamic size !")
|
||||
ProcedureReturn #MsgPack_InvalidSize
|
||||
EndIf
|
||||
|
||||
@@ -76,7 +77,7 @@ EndProcedure
|
||||
; Remark: Doesn't apply to Int/UInt variants since those have their own function for extracting the value.
|
||||
Procedure.q _MsgPackGetFieldDynamicSize_4After(*MsgPackData.MsgPackData)
|
||||
If Not _MsgPackHasSpaceLeft(*MsgPackData, 1 + 4)
|
||||
DebuggerWarning("We have less than 4 bytes to read the field's dynamic size !")
|
||||
Debug("We have less than 4 bytes to read the field's dynamic size !")
|
||||
ProcedureReturn #MsgPack_InvalidSize
|
||||
EndIf
|
||||
|
||||
@@ -84,7 +85,9 @@ Procedure.q _MsgPackGetFieldDynamicSize_4After(*MsgPackData.MsgPackData)
|
||||
EndProcedure
|
||||
|
||||
; Returns a given format code's spacing info based on itself and the data in the given MsgPack buffer.
|
||||
; ???
|
||||
; `*VariableSizeQ` receives the size of str/bin/ext data, or the number of entries in arrays/maps.
|
||||
; `*ValueStartOffsetB` receives the offset of the value from the format code, ext type excluded.
|
||||
; Both receive `#MsgPack_InvalidSize` and `#False` is returned for unsupported format codes.
|
||||
; Remark: Doesn't apply to FixInt/Int/UInt variants since those have their own function for extracting the value.
|
||||
Procedure _MsgPackGetFieldSpacingInfo(*MsgPackData.MsgPackData, ExaminedDataType.MsgPack_FormatCode, *VariableSizeQ = #Null, *ValueStartOffsetB = #Null)
|
||||
CompilerIf Not #MsgPack_DisableNullChecks
|
||||
@@ -100,23 +103,12 @@ Procedure _MsgPackGetFieldSpacingInfo(*MsgPackData.MsgPackData, ExaminedDataType
|
||||
|
||||
; The "within" sizes are read from the format code, which must be in the buffer.
|
||||
If _MsgPackIsAtTheEnd(*MsgPackData)
|
||||
DebuggerWarning("End of buffer reached, cannot read the field's dynamic size !")
|
||||
Debug("End of buffer reached, cannot read the field's dynamic size !")
|
||||
ExaminedDataType = #MsgPack_FormatCode_Invalid
|
||||
EndIf
|
||||
|
||||
Select ExaminedDataType
|
||||
Case #MsgPack_FormatCode_FixIntPositive:
|
||||
If *VariableSizeQ <> #Null
|
||||
PokeQ(*VariableSizeQ, _MsgPackGetFieldDynamicSize_Within(*MsgPackData, %01111111))
|
||||
EndIf
|
||||
If *ValueStartOffsetB <> #Null
|
||||
; FIXME: Is this right ?!?
|
||||
PokeB(*ValueStartOffsetB, 1)
|
||||
EndIf
|
||||
ProcedureReturn #True
|
||||
|
||||
Case #MsgPack_FormatCode_FixStr,
|
||||
#MsgPack_FormatCode_FixIntNegative:
|
||||
Case #MsgPack_FormatCode_FixStr:
|
||||
If *VariableSizeQ <> #Null
|
||||
PokeQ(*VariableSizeQ, _MsgPackGetFieldDynamicSize_Within(*MsgPackData, %00011111))
|
||||
EndIf
|
||||
@@ -187,89 +179,213 @@ Procedure _MsgPackGetFieldSpacingInfo(*MsgPackData.MsgPackData, ExaminedDataType
|
||||
EndProcedure
|
||||
|
||||
|
||||
;-> > Public
|
||||
;-> Field layout
|
||||
|
||||
; NOTE: Not sure if making this public is useful :/
|
||||
Procedure.q MsgPackGetFieldDynamicSize(*MsgPackData.MsgPackData, ExaminedDataType.MsgPack_FormatCode)
|
||||
Protected ReturnedValue.q = 0
|
||||
_MsgPackGetFieldSpacingInfo(*MsgPackData, ExaminedDataType, @ReturnedValue, #Null)
|
||||
ProcedureReturn ReturnedValue
|
||||
;-> > Private
|
||||
|
||||
; Reads the layout of the field at the current offset, without moving it.
|
||||
; On success:
|
||||
; * `*HeaderSize` receives the length of its format code, size field and ext type.
|
||||
; * `*DataSize` receives the length of the data or value following its header.
|
||||
; * `*ChildCount` receives the number of fields contained in an array or map, keys included.
|
||||
; The header and data are guaranteed to be within the buffer, but contained fields aren't checked.
|
||||
; Sets the structure's error code and returns it.
|
||||
Procedure.MsgPack_ErrorCode _MsgPackGetFieldLayout(*MsgPackData.MsgPackData, *HeaderSize.Integer, *DataSize.Quad, *ChildCount.Quad)
|
||||
Protected FormatCode.MsgPack_FormatCode
|
||||
Protected DataType.MsgPack_DataType
|
||||
Protected ChildCount.q
|
||||
Protected ValueStartOffset.b
|
||||
|
||||
CompilerIf Not #MsgPack_DisableNullChecks
|
||||
If Not *MsgPackData
|
||||
DebuggerError("A #Null MsgPackData pointer was passed !")
|
||||
ProcedureReturn #MsgPack_Error_NullPointerGiven
|
||||
EndIf
|
||||
CompilerEndIf
|
||||
|
||||
If _MsgPackIsAtTheEnd(*MsgPackData)
|
||||
Debug("End of buffer reached, cannot check data format code !")
|
||||
*MsgPackData\LastError = #MsgPack_Error_AtOrPastTheEndOfBuffer
|
||||
ProcedureReturn #MsgPack_Error_AtOrPastTheEndOfBuffer
|
||||
EndIf
|
||||
|
||||
FormatCode = MsgPackGetFormatCode(*MsgPackData)
|
||||
DataType = MsgPackFormatCodeToDataType(FormatCode)
|
||||
|
||||
*HeaderSize\i = 1
|
||||
*DataSize\q = 0
|
||||
*ChildCount\q = 0
|
||||
|
||||
Select DataType
|
||||
Case #MsgPack_DataTypes_Null,
|
||||
#MsgPack_DataTypes_Boolean,
|
||||
#MsgPack_DataTypes_Integer,
|
||||
#MsgPack_DataTypes_Float:
|
||||
; Fixints, nil and bools only have their format code.
|
||||
Select FormatCode
|
||||
Case #MsgPack_FormatCode_UInt8,
|
||||
#MsgPack_FormatCode_Int8:
|
||||
*DataSize\q = 1
|
||||
Case #MsgPack_FormatCode_UInt16,
|
||||
#MsgPack_FormatCode_Int16:
|
||||
*DataSize\q = 2
|
||||
Case #MsgPack_FormatCode_UInt32,
|
||||
#MsgPack_FormatCode_Int32,
|
||||
#MsgPack_FormatCode_Float32:
|
||||
*DataSize\q = 4
|
||||
Case #MsgPack_FormatCode_UInt64,
|
||||
#MsgPack_FormatCode_Int64,
|
||||
#MsgPack_FormatCode_Float64:
|
||||
*DataSize\q = 8
|
||||
EndSelect
|
||||
|
||||
Case #MsgPack_DataTypes_String,
|
||||
#MsgPack_DataTypes_Binary,
|
||||
#MsgPack_DataTypes_Extension:
|
||||
; Also checks that the whole field is within the buffer.
|
||||
ProcedureReturn _MsgPackReadSizedFieldHeader(*MsgPackData, DataType, *HeaderSize, *DataSize)
|
||||
|
||||
Case #MsgPack_DataTypes_Array,
|
||||
#MsgPack_DataTypes_Map:
|
||||
_MsgPackGetFieldSpacingInfo(*MsgPackData, FormatCode, @ChildCount, @ValueStartOffset)
|
||||
If ChildCount = #MsgPack_InvalidSize
|
||||
Debug("End of buffer reached, cannot read the field's size !")
|
||||
*MsgPackData\LastError = #MsgPack_Error_BufferTooSmall
|
||||
ProcedureReturn #MsgPack_Error_BufferTooSmall
|
||||
EndIf
|
||||
|
||||
If DataType = #MsgPack_DataTypes_Map
|
||||
ChildCount = ChildCount * 2
|
||||
EndIf
|
||||
|
||||
*HeaderSize\i = ValueStartOffset
|
||||
*ChildCount\q = ChildCount
|
||||
|
||||
Default:
|
||||
Debug("Invalid format code ! (" + RSet(Hex(PeekA(*MsgPackData\Buffer + *MsgPackData\BufferOffset)), 2, "0") + ")")
|
||||
*MsgPackData\LastError = #MsgPack_Error_InvalidFormatCode
|
||||
ProcedureReturn #MsgPack_Error_InvalidFormatCode
|
||||
EndSelect
|
||||
|
||||
If Not _MsgPackHasSpaceLeft(*MsgPackData, *HeaderSize\i + *DataSize\q)
|
||||
Debug("End of buffer reached, cannot read out of bounds !")
|
||||
*MsgPackData\LastError = #MsgPack_Error_BufferTooSmall
|
||||
ProcedureReturn #MsgPack_Error_BufferTooSmall
|
||||
EndIf
|
||||
|
||||
*MsgPackData\LastError = #MsgPack_Error_Success
|
||||
ProcedureReturn #MsgPack_Error_Success
|
||||
EndProcedure
|
||||
|
||||
|
||||
; Moves the offset past the next `FieldCount` fields, the ones contained in arrays and maps included.
|
||||
; Done iteratively so deeply nested arrays and maps can't overflow the stack.
|
||||
; The offset is left untouched on errors.
|
||||
; Sets the structure's error code and returns it.
|
||||
Procedure.MsgPack_ErrorCode _MsgPackSkipFields(*MsgPackData.MsgPackData, FieldCount.q)
|
||||
Protected StartOffset.i
|
||||
Protected HeaderSize.i
|
||||
Protected DataSize.q
|
||||
Protected ChildCount.q
|
||||
Protected ErrorCode.MsgPack_ErrorCode = #MsgPack_Error_Success
|
||||
|
||||
CompilerIf Not #MsgPack_DisableNullChecks
|
||||
If Not *MsgPackData
|
||||
DebuggerError("A #Null MsgPackData pointer was passed !")
|
||||
ProcedureReturn #MsgPack_Error_NullPointerGiven
|
||||
EndIf
|
||||
CompilerEndIf
|
||||
|
||||
StartOffset = *MsgPackData\BufferOffset
|
||||
|
||||
While FieldCount > 0
|
||||
ErrorCode = _MsgPackGetFieldLayout(*MsgPackData, @HeaderSize, @DataSize, @ChildCount)
|
||||
If ErrorCode <> #MsgPack_Error_Success
|
||||
Break
|
||||
EndIf
|
||||
|
||||
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + HeaderSize + DataSize
|
||||
FieldCount = FieldCount - 1 + ChildCount
|
||||
|
||||
; Each field takes at least 1 byte, which rejects bogus array and map sizes early.
|
||||
If FieldCount > *MsgPackData\BufferSize - *MsgPackData\BufferOffset
|
||||
Debug("End of buffer reached, " + Str(FieldCount) + " fields are left to skip !")
|
||||
*MsgPackData\LastError = #MsgPack_Error_BufferTooSmall
|
||||
ErrorCode = #MsgPack_Error_BufferTooSmall
|
||||
Break
|
||||
EndIf
|
||||
Wend
|
||||
|
||||
If ErrorCode <> #MsgPack_Error_Success
|
||||
*MsgPackData\BufferOffset = StartOffset
|
||||
ProcedureReturn ErrorCode
|
||||
EndIf
|
||||
|
||||
*MsgPackData\LastError = #MsgPack_Error_Success
|
||||
ProcedureReturn #MsgPack_Error_Success
|
||||
EndProcedure
|
||||
|
||||
|
||||
;-> Size getters
|
||||
|
||||
; Returns the full size in bytes of the field at the current offset, without moving it.
|
||||
; Arrays and maps include all of their contained fields, recursively.
|
||||
; OnError: Returns `#MsgPack_InvalidSize` and sets the `MsgPackData\LastError` field.
|
||||
Procedure.q MsgPackGetFieldFullSize(*MsgPackData.MsgPackData)
|
||||
Protected IsIterable = #False
|
||||
Protected HasVariableSize = #False
|
||||
|
||||
Protected FieldVariableSize.q = #MsgPack_InvalidSize
|
||||
Protected FieldValueOffset.b = #MsgPack_InvalidSize
|
||||
Protected FullFieldSize.q = 0
|
||||
Protected StartOffset.i
|
||||
Protected FullSize.q
|
||||
|
||||
Protected ExaminedFormatCode.MsgPack_FormatCode
|
||||
|
||||
; Safety checks
|
||||
CompilerIf Not #MsgPack_DisableNullChecks
|
||||
If Not *MsgPackData
|
||||
DebuggerError("A #Null MsgPackData pointer was passed !")
|
||||
ProcedureReturn #MsgPack_Error_NullPointerGiven
|
||||
ProcedureReturn #MsgPack_InvalidSize
|
||||
EndIf
|
||||
CompilerEndIf
|
||||
|
||||
; Preparing stuff
|
||||
*MsgPackData\LastError = #MsgPack_Error_Success
|
||||
ExaminedFormatCode = MsgPackGetFormatCode(*MsgPackData)
|
||||
|
||||
; Determining iterability for later
|
||||
If (ExaminedFormatCode >= #MsgPack_FormatCode_Array16 And ExaminedFormatCode <= #MsgPack_FormatCode_Map32) Or
|
||||
(ExaminedFormatCode = #MsgPack_FormatCode_FixArray Or ExaminedFormatCode = #MsgPack_FormatCode_FixMap)
|
||||
IsIterable = #True
|
||||
EndIf
|
||||
|
||||
; We ignore the returned boolean since we maw not care about this result later down the line.
|
||||
_MsgPackGetFieldSpacingInfo(*MsgPackData, ExaminedFormatCode, @FieldVariableSize, @FieldValueOffset)
|
||||
|
||||
If IsIterable
|
||||
; Iterables, we'll have to go recursive
|
||||
; They're also all using variable sizes.
|
||||
DebuggerError("Getting an iterable field's full size isn't implemented yet !")
|
||||
|
||||
ElseIf FieldVariableSize = -1
|
||||
; Simple fixed-size field, we'll have to determine the size
|
||||
|
||||
Else
|
||||
; Variable size field
|
||||
|
||||
EndIf
|
||||
|
||||
; Step 3
|
||||
ProcedureReturn FullFieldSize
|
||||
|
||||
StartOffset = *MsgPackData\BufferOffset
|
||||
|
||||
If _MsgPackSkipFields(*MsgPackData, 1) <> #MsgPack_Error_Success
|
||||
ProcedureReturn #MsgPack_InvalidSize
|
||||
EndIf
|
||||
|
||||
FullSize = *MsgPackData\BufferOffset - StartOffset
|
||||
*MsgPackData\BufferOffset = StartOffset
|
||||
|
||||
ProcedureReturn FullSize
|
||||
EndProcedure
|
||||
|
||||
|
||||
;-> General
|
||||
; Returns the number of entries in the array or map at the current offset, without moving it.
|
||||
; Map entries are made of a key and a value, and count as one.
|
||||
; OnError: Returns `#MsgPack_InvalidSize` and sets the `MsgPackData\LastError` field.
|
||||
Procedure.q MsgPackGetEntryCount(*MsgPackData.MsgPackData)
|
||||
Protected HeaderSize.i
|
||||
Protected DataSize.q
|
||||
Protected ChildCount.q
|
||||
|
||||
; TODO: Use the internal flag to detect if we should check the remaining count.
|
||||
; MsgPackNext(MaximalDepth)
|
||||
If _MsgPackGetFieldLayout(*MsgPackData, @HeaderSize, @DataSize, @ChildCount) <> #MsgPack_Error_Success
|
||||
ProcedureReturn #MsgPack_InvalidSize
|
||||
EndIf
|
||||
|
||||
Select MsgPackFormatCodeToDataType(MsgPackGetFormatCode(*MsgPackData))
|
||||
Case #MsgPack_DataTypes_Array:
|
||||
ProcedureReturn ChildCount
|
||||
Case #MsgPack_DataTypes_Map:
|
||||
ProcedureReturn ChildCount / 2
|
||||
EndSelect
|
||||
|
||||
Debug("Unexpected format code ! (" + RSet(Hex(PeekA(*MsgPackData\Buffer + *MsgPackData\BufferOffset)), 2, "0") + ")")
|
||||
*MsgPackData\LastError = #MsgPack_Error_InvalidFormatCode
|
||||
ProcedureReturn #MsgPack_InvalidSize
|
||||
EndProcedure
|
||||
|
||||
|
||||
;-> Dictionary
|
||||
;-> Navigation
|
||||
|
||||
; MsgPackExamineDictionary
|
||||
|
||||
Macro MsgPackFinishDictionary(MsgPackData) : MsgPackFree(MsgPackData, #False) : EndMacro
|
||||
|
||||
|
||||
;-> Arrays
|
||||
|
||||
Procedure.i MsgPackExamineArray(*MsgPackData.MsgPackData, ExaminationOptions.MsgPack_ExaminationOption = 0)
|
||||
Protected ExaminedFormatCode.MsgPack_FormatCode
|
||||
Protected ExaminedDataType.MsgPack_FormatCode
|
||||
Protected ExaminedArraySize.q
|
||||
|
||||
Protected *ExaminedData.MsgPackData = #Null
|
||||
|
||||
; Moves the offset past the next `FieldCount` fields, the ones contained in arrays and maps included.
|
||||
; Map keys and values count as separate fields.
|
||||
; Returns `#MsgPack_Error_Success` on success, something else otherwise, in which case the offset isn't moved.
|
||||
Procedure.MsgPack_ErrorCode MsgPackSkip(*MsgPackData.MsgPackData, FieldCount.q = 1)
|
||||
CompilerIf Not #MsgPack_DisableNullChecks
|
||||
If Not *MsgPackData
|
||||
DebuggerError("A #Null MsgPackData pointer was passed !")
|
||||
@@ -277,54 +393,369 @@ Procedure.i MsgPackExamineArray(*MsgPackData.MsgPackData, ExaminationOptions.Msg
|
||||
EndIf
|
||||
CompilerEndIf
|
||||
|
||||
*MsgPackData\LastError = #MsgPack_Error_Success
|
||||
If FieldCount < 0
|
||||
DebuggerWarning("Cannot skip a negative amount of fields !")
|
||||
*MsgPackData\LastError = #MsgPack_Error_InvalidValue
|
||||
ProcedureReturn #MsgPack_Error_InvalidValue
|
||||
EndIf
|
||||
|
||||
ExaminedFormatCode = MsgPackGetFormatCode(*MsgPackData)
|
||||
ExaminedDataType = MsgPackGetFormatCode(ExaminedFormatCode)
|
||||
|
||||
If ExaminedDataType <> #MsgPack_DataTypes_Array
|
||||
DebuggerWarning("Attempted to iterate on format code 0x" +
|
||||
RSet(Hex(ExaminedFormatCode, #PB_Ascii), 2, "0") +
|
||||
" as if it was an array !")
|
||||
*MsgPackData\LastError = -1
|
||||
ProcedureReturn #Null
|
||||
EndIf
|
||||
|
||||
Select ExaminedFormatCode
|
||||
Case #MsgPack_FormatCode_FixArray:
|
||||
ExaminedArraySize = ExaminedFormatCode & $000000000000000F
|
||||
|
||||
Case #MsgPack_FormatCode_Array16:
|
||||
If Not _MsgPackHasSpaceLeft(*MsgPackData, *MsgPackData\BufferSize + 3)
|
||||
DebuggerWarning("We have less than 3 bytes to read the Array16's size !")
|
||||
; FIXME: Use the proper one
|
||||
*MsgPackData\LastError = -1
|
||||
ProcedureReturn #Null
|
||||
EndIf
|
||||
|
||||
ExaminedArraySize = PeekU(*MsgPackData\Buffer + 1)
|
||||
|
||||
Case #MsgPack_FormatCode_Array32:
|
||||
If Not _MsgPackHasSpaceLeft(*MsgPackData, *MsgPackData\BufferSize + 5)
|
||||
DebuggerWarning("We have less than 5 bytes to read the Array32's size !")
|
||||
; FIXME: Use the proper one
|
||||
*MsgPackData\LastError = -1
|
||||
ProcedureReturn #Null
|
||||
EndIf
|
||||
|
||||
; FIXME: WILL CAUSE ISSUES WITH UNSIGNED SHIT !!!
|
||||
ExaminedArraySize = PeekI(*MsgPackData\Buffer + 1)
|
||||
|
||||
Default:
|
||||
; ???
|
||||
EndSelect
|
||||
|
||||
; ???
|
||||
|
||||
|
||||
; Not implemented yet !
|
||||
ProcedureReturn #Null
|
||||
ProcedureReturn _MsgPackSkipFields(*MsgPackData, FieldCount)
|
||||
EndProcedure
|
||||
|
||||
|
||||
Macro MsgPackFinishArray(MsgPackData) : MsgPackFree(MsgPackData, #False) : EndMacro
|
||||
;-> Iterators
|
||||
|
||||
; Prepares a view over the array or map at the current offset, to iterate on it with `MsgPackNextEntry()`.
|
||||
; The view reads straight from the given structure's buffer, which must not be grown or freed while it is used.
|
||||
; Its size is limited to the array or map's content, so reads can't go past it.
|
||||
; The whole array or map is checked beforehand, and the offset is moved past it.
|
||||
; `#MsgPack_ExaminationOptions_Unsafe` skips that check, which isn't recommended, see its definition.
|
||||
; Returns: A view that must be freed with `MsgPackFinish()`.
|
||||
; OnError: Returns `#Null` and sets the `MsgPackData\LastError` field.
|
||||
Procedure.i MsgPackExamine(*MsgPackData.MsgPackData, ExaminationOptions.MsgPack_ExaminationOption = 0)
|
||||
Protected DataType.MsgPack_DataType
|
||||
Protected HeaderSize.i
|
||||
Protected DataSize.q
|
||||
Protected ChildCount.q
|
||||
Protected StartOffset.i
|
||||
Protected EndOffset.i
|
||||
Protected IsUnsafe.b
|
||||
Protected *View.MsgPackData
|
||||
|
||||
CompilerIf Not #MsgPack_DisableNullChecks
|
||||
If Not *MsgPackData
|
||||
DebuggerError("A #Null MsgPackData pointer was passed !")
|
||||
ProcedureReturn #Null
|
||||
EndIf
|
||||
CompilerEndIf
|
||||
|
||||
If _MsgPackGetFieldLayout(*MsgPackData, @HeaderSize, @DataSize, @ChildCount) <> #MsgPack_Error_Success
|
||||
ProcedureReturn #Null
|
||||
EndIf
|
||||
|
||||
DataType = MsgPackFormatCodeToDataType(MsgPackGetFormatCode(*MsgPackData))
|
||||
If DataType <> #MsgPack_DataTypes_Array And DataType <> #MsgPack_DataTypes_Map
|
||||
Debug("Attempted to examine format code 0x" +
|
||||
RSet(Hex(PeekA(*MsgPackData\Buffer + *MsgPackData\BufferOffset)), 2, "0") +
|
||||
" as if it was an array or map !")
|
||||
*MsgPackData\LastError = #MsgPack_Error_InvalidFormatCode
|
||||
ProcedureReturn #Null
|
||||
EndIf
|
||||
|
||||
StartOffset = *MsgPackData\BufferOffset
|
||||
IsUnsafe = Bool(ExaminationOptions & #MsgPack_ExaminationOptions_Unsafe)
|
||||
|
||||
If IsUnsafe
|
||||
; The end is unknown, the view is only limited by `IteratorFieldsLeft` until it is reached.
|
||||
EndOffset = *MsgPackData\BufferSize
|
||||
Else
|
||||
; Seeking the end of the array or map, which also checks all of its content.
|
||||
If _MsgPackSkipFields(*MsgPackData, 1) <> #MsgPack_Error_Success
|
||||
ProcedureReturn #Null
|
||||
EndIf
|
||||
EndOffset = *MsgPackData\BufferOffset
|
||||
EndIf
|
||||
|
||||
*View = AllocateStructure(MsgPackData)
|
||||
If Not *View
|
||||
Debug("Failed to allocate memory for the view !")
|
||||
*MsgPackData\BufferOffset = StartOffset
|
||||
*MsgPackData\LastError = #MsgPack_Error_FailedToAllocateBuffer
|
||||
ProcedureReturn #Null
|
||||
EndIf
|
||||
|
||||
*View\Buffer = *MsgPackData\Buffer + StartOffset + HeaderSize
|
||||
*View\BufferSize = EndOffset - StartOffset - HeaderSize
|
||||
*View\BufferOffset = 0
|
||||
*View\BufferGrowthIncrement = 1
|
||||
*View\DataFlags = #_MsgPack_ViewDataFlags
|
||||
*View\IteratorEntryOffset = #MsgPack_InvalidSize
|
||||
*View\IteratorFieldsLeft = ChildCount
|
||||
|
||||
If DataType = #MsgPack_DataTypes_Map
|
||||
*View\DataFlags = *View\DataFlags | #MsgPack_DataFlags_CanProvideKeys
|
||||
EndIf
|
||||
|
||||
If IsUnsafe Or Not _MsgPackHasFlag(*MsgPackData, #MsgPack_DataFlags_MoveOffsetAfterRead)
|
||||
*MsgPackData\BufferOffset = StartOffset
|
||||
EndIf
|
||||
|
||||
*MsgPackData\LastError = #MsgPack_Error_Success
|
||||
ProcedureReturn *View
|
||||
EndProcedure
|
||||
|
||||
|
||||
; Moves a view's offset to its next entry, skipping whatever wasn't read from the current one.
|
||||
; The entry can then be read with the regular procedures, key first for maps.
|
||||
; Nested arrays and maps can be examined or left as-is, they'll be skipped.
|
||||
; Returns: `#True` if an entry is available, `#False` once the end of the view is reached.
|
||||
; OnError: Returns `#False` and sets the `MsgPackData\LastError` field.
|
||||
Procedure.b MsgPackNextEntry(*Iterator.MsgPackData)
|
||||
Protected FieldsPerEntry.q = 1
|
||||
|
||||
CompilerIf Not #MsgPack_DisableNullChecks
|
||||
If Not *Iterator
|
||||
DebuggerError("A #Null MsgPackData pointer was passed !")
|
||||
ProcedureReturn #False
|
||||
EndIf
|
||||
CompilerEndIf
|
||||
|
||||
If Not _MsgPackHasFlag(*Iterator, #MsgPack_DataFlags_Iterator)
|
||||
DebuggerError("The given MsgPackData isn't a view from `MsgPackExamine()` !")
|
||||
*Iterator\LastError = #MsgPack_Error_InvalidValue
|
||||
ProcedureReturn #False
|
||||
EndIf
|
||||
|
||||
If *Iterator\IteratorEntryOffset = #MsgPack_InvalidSize
|
||||
; First call, the first entry is at the very start of the view.
|
||||
*Iterator\IteratorEntryOffset = 0
|
||||
ElseIf *Iterator\IteratorFieldsLeft > 0
|
||||
; Skipping from the entry's start, regardless of what was read from it.
|
||||
If _MsgPackHasFlag(*Iterator, #MsgPack_DataFlags_CanProvideKeys)
|
||||
FieldsPerEntry = 2
|
||||
EndIf
|
||||
|
||||
*Iterator\BufferOffset = *Iterator\IteratorEntryOffset
|
||||
If _MsgPackSkipFields(*Iterator, FieldsPerEntry) <> #MsgPack_Error_Success
|
||||
ProcedureReturn #False
|
||||
EndIf
|
||||
*Iterator\IteratorEntryOffset = *Iterator\BufferOffset
|
||||
*Iterator\IteratorFieldsLeft = *Iterator\IteratorFieldsLeft - FieldsPerEntry
|
||||
EndIf
|
||||
|
||||
*Iterator\BufferOffset = *Iterator\IteratorEntryOffset
|
||||
*Iterator\LastError = #MsgPack_Error_Success
|
||||
|
||||
If *Iterator\IteratorFieldsLeft <= 0
|
||||
; Unsafe views only find their end here, so reads can't go past it afterward.
|
||||
*Iterator\BufferSize = *Iterator\IteratorEntryOffset
|
||||
ProcedureReturn #False
|
||||
EndIf
|
||||
|
||||
ProcedureReturn #True
|
||||
EndProcedure
|
||||
|
||||
|
||||
Macro MsgPackFinish(Iterator) : MsgPackFree(Iterator, #False) : EndMacro
|
||||
|
||||
|
||||
;-> Setters
|
||||
|
||||
;-> > Private
|
||||
|
||||
; Used in place of a size field's length to pick the smallest one that fits.
|
||||
#_MsgPack_SmallestSizeField = $FF
|
||||
|
||||
; Counts the fields in the `BufferSize` bytes at `*Buffer`, the ones contained in arrays and maps excluded.
|
||||
; The whole data is checked, and must end on a field's boundary.
|
||||
; Returns: The number of fields.
|
||||
; OnError: Returns `#MsgPack_InvalidSize` and sets the `MsgPackData\LastError` field.
|
||||
Procedure.q _MsgPackCountFields(*MsgPackData.MsgPackData, *Buffer, BufferSize.q)
|
||||
Protected Source.MsgPackData
|
||||
Protected FieldCount.q = 0
|
||||
|
||||
If BufferSize < 0
|
||||
DebuggerWarning("Received negative size !")
|
||||
*MsgPackData\LastError = #MsgPack_Error_BinaryDataSizeIsNegative
|
||||
ProcedureReturn #MsgPack_InvalidSize
|
||||
ElseIf BufferSize > #_MsgPack_MaxIntegerValue
|
||||
DebuggerWarning("Requested size can't be addressed: " + Str(BufferSize))
|
||||
*MsgPackData\LastError = #MsgPack_Error_BinaryDataSizeIsTooBig
|
||||
ProcedureReturn #MsgPack_InvalidSize
|
||||
EndIf
|
||||
|
||||
CompilerIf Not #MsgPack_DisableNullChecks
|
||||
If BufferSize > 0 And Not *Buffer
|
||||
DebuggerError("A #Null data buffer was passed !")
|
||||
*MsgPackData\LastError = #MsgPack_Error_NullPointerGiven
|
||||
ProcedureReturn #MsgPack_InvalidSize
|
||||
EndIf
|
||||
CompilerEndIf
|
||||
|
||||
; Temporary wrapper, so the regular procedures can walk the data.
|
||||
Source\Buffer = *Buffer
|
||||
Source\BufferSize = BufferSize
|
||||
|
||||
While Not _MsgPackIsAtTheEnd(Source)
|
||||
If _MsgPackSkipFields(@Source, 1) <> #MsgPack_Error_Success
|
||||
*MsgPackData\LastError = Source\LastError
|
||||
ProcedureReturn #MsgPack_InvalidSize
|
||||
EndIf
|
||||
FieldCount = FieldCount + 1
|
||||
Wend
|
||||
|
||||
ProcedureReturn FieldCount
|
||||
EndProcedure
|
||||
|
||||
|
||||
; Writes an array or map whose content is a copy of the `BufferSize` bytes of MessagePack data at `*Buffer`.
|
||||
; Map content must hold an even number of fields, alternating keys and values.
|
||||
; `SizeFieldByteLength` follows the "sized field" helpers' convention, `0` being FixArray and FixMap,
|
||||
; or is `#_MsgPack_SmallestSizeField` to use the smallest format code that fits.
|
||||
; Sets the structure's error code and returns it.
|
||||
Procedure.MsgPack_ErrorCode _MsgPackWriteContainer(*MsgPackData.MsgPackData, IsMap.b, SizeFieldByteLength.a, *Buffer, BufferSize.q)
|
||||
Protected EntryCount.q
|
||||
Protected MaxEntryCount.q
|
||||
Protected FormatCode.MsgPack_FormatCode
|
||||
Protected ErrorCode.MsgPack_ErrorCode
|
||||
|
||||
CompilerIf Not #MsgPack_DisableNullChecks
|
||||
If Not *MsgPackData
|
||||
DebuggerError("A #Null MsgPackData pointer was passed !")
|
||||
ProcedureReturn #MsgPack_Error_NullPointerGiven
|
||||
EndIf
|
||||
CompilerEndIf
|
||||
|
||||
; The content's address would be invalidated when growing.
|
||||
If *Buffer And *Buffer >= *MsgPackData\Buffer And *Buffer < *MsgPackData\Buffer + *MsgPackData\BufferSize
|
||||
DebuggerError("Cannot write content located in the destination's own buffer !")
|
||||
*MsgPackData\LastError = #MsgPack_Error_InvalidValue
|
||||
ProcedureReturn #MsgPack_Error_InvalidValue
|
||||
EndIf
|
||||
|
||||
EntryCount = _MsgPackCountFields(*MsgPackData, *Buffer, BufferSize)
|
||||
If EntryCount = #MsgPack_InvalidSize
|
||||
ProcedureReturn *MsgPackData\LastError
|
||||
EndIf
|
||||
|
||||
If IsMap
|
||||
If EntryCount % 2 <> 0
|
||||
Debug("Map content has an odd number of fields (" + Str(EntryCount) + "), keys and values must be paired !")
|
||||
*MsgPackData\LastError = #MsgPack_Error_OddMapFieldCount
|
||||
ProcedureReturn #MsgPack_Error_OddMapFieldCount
|
||||
EndIf
|
||||
EntryCount = EntryCount / 2
|
||||
EndIf
|
||||
|
||||
If SizeFieldByteLength = #_MsgPack_SmallestSizeField
|
||||
If EntryCount <= 15
|
||||
SizeFieldByteLength = 0
|
||||
ElseIf EntryCount <= 65535
|
||||
SizeFieldByteLength = 2
|
||||
Else
|
||||
SizeFieldByteLength = 4
|
||||
EndIf
|
||||
EndIf
|
||||
|
||||
Select SizeFieldByteLength
|
||||
Case 0 :
|
||||
MaxEntryCount = 15
|
||||
FormatCode = #MsgPack_FormatCode_FixArray
|
||||
If IsMap : FormatCode = #MsgPack_FormatCode_FixMap : EndIf
|
||||
Case 2 :
|
||||
MaxEntryCount = 65535
|
||||
FormatCode = #MsgPack_FormatCode_Array16
|
||||
If IsMap : FormatCode = #MsgPack_FormatCode_Map16 : EndIf
|
||||
Case 4 :
|
||||
MaxEntryCount = 4294967295
|
||||
FormatCode = #MsgPack_FormatCode_Array32
|
||||
If IsMap : FormatCode = #MsgPack_FormatCode_Map32 : EndIf
|
||||
Default
|
||||
DebuggerError("Invalid size field length: " + Str(SizeFieldByteLength))
|
||||
*MsgPackData\LastError = #MsgPack_Error_InvalidValue
|
||||
ProcedureReturn #MsgPack_Error_InvalidValue
|
||||
EndSelect
|
||||
|
||||
If EntryCount > MaxEntryCount
|
||||
DebuggerWarning("Content has more than " + Str(MaxEntryCount) + " entries !")
|
||||
*MsgPackData\LastError = #MsgPack_Error_TooManyEntries
|
||||
ProcedureReturn #MsgPack_Error_TooManyEntries
|
||||
EndIf
|
||||
|
||||
ErrorCode = _MsgPackReserveWrite(*MsgPackData, 1 + SizeFieldByteLength + BufferSize)
|
||||
If ErrorCode <> #MsgPack_Error_Success
|
||||
ProcedureReturn ErrorCode
|
||||
EndIf
|
||||
|
||||
; Each field takes at least 1 byte, so the header won't reserve more than what was reserved above.
|
||||
ErrorCode = _MsgPackWriteSizedFieldHeader(*MsgPackData, FormatCode, SizeFieldByteLength, EntryCount)
|
||||
If ErrorCode <> #MsgPack_Error_Success
|
||||
ProcedureReturn ErrorCode
|
||||
EndIf
|
||||
|
||||
If BufferSize > 0
|
||||
CopyMemory(*Buffer, *MsgPackData\Buffer + *MsgPackData\BufferOffset, BufferSize)
|
||||
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + BufferSize
|
||||
EndIf
|
||||
_MsgPackRewindOffsetAfterWrite(*MsgPackData, 1 + SizeFieldByteLength + BufferSize)
|
||||
|
||||
ProcedureReturn #MsgPack_Error_Success
|
||||
EndProcedure
|
||||
|
||||
|
||||
;-> > Public
|
||||
|
||||
; Writes an array whose entries are the fields in the `BufferSize` bytes of MessagePack data at `*Buffer`,
|
||||
; using the smallest array format code that fits.
|
||||
; `*Buffer` can come from `MsgPackFinalize()`, with `MemorySize()` as its size.
|
||||
; Returns `#MsgPack_Error_Success` on success, something else otherwise.
|
||||
Procedure.MsgPack_ErrorCode MsgPackWriteArray(*MsgPackData.MsgPackData, *Buffer, BufferSize.q)
|
||||
ProcedureReturn _MsgPackWriteContainer(*MsgPackData, #False, #_MsgPack_SmallestSizeField, *Buffer, BufferSize)
|
||||
EndProcedure
|
||||
|
||||
|
||||
; Same as `MsgPackWriteArray()`, for maps.
|
||||
; The data must hold an even number of fields, alternating keys and values.
|
||||
; Returns `#MsgPack_Error_Success` on success, something else otherwise.
|
||||
Procedure.MsgPack_ErrorCode MsgPackWriteMap(*MsgPackData.MsgPackData, *Buffer, BufferSize.q)
|
||||
ProcedureReturn _MsgPackWriteContainer(*MsgPackData, #True, #_MsgPack_SmallestSizeField, *Buffer, BufferSize)
|
||||
EndProcedure
|
||||
|
||||
|
||||
; Same as `MsgPackWriteArray()`, with the data written so far in `*Source`.
|
||||
; `*Source` is left untouched and must be freed by the caller.
|
||||
; Returns `#MsgPack_Error_Success` on success, something else otherwise.
|
||||
Procedure.MsgPack_ErrorCode MsgPackWriteArrayFrom(*MsgPackData.MsgPackData, *Source.MsgPackData)
|
||||
CompilerIf Not #MsgPack_DisableNullChecks
|
||||
If Not *MsgPackData Or Not *Source
|
||||
DebuggerError("A #Null MsgPackData pointer was passed !")
|
||||
ProcedureReturn #MsgPack_Error_NullPointerGiven
|
||||
EndIf
|
||||
CompilerEndIf
|
||||
|
||||
ProcedureReturn MsgPackWriteArray(*MsgPackData, *Source\Buffer, *Source\BufferOffset)
|
||||
EndProcedure
|
||||
|
||||
|
||||
; Same as `MsgPackWriteMap()`, with the data written so far in `*Source`.
|
||||
; `*Source` is left untouched and must be freed by the caller.
|
||||
; Returns `#MsgPack_Error_Success` on success, something else otherwise.
|
||||
Procedure.MsgPack_ErrorCode MsgPackWriteMapFrom(*MsgPackData.MsgPackData, *Source.MsgPackData)
|
||||
CompilerIf Not #MsgPack_DisableNullChecks
|
||||
If Not *MsgPackData Or Not *Source
|
||||
DebuggerError("A #Null MsgPackData pointer was passed !")
|
||||
ProcedureReturn #MsgPack_Error_NullPointerGiven
|
||||
EndIf
|
||||
CompilerEndIf
|
||||
|
||||
ProcedureReturn MsgPackWriteMap(*MsgPackData, *Source\Buffer, *Source\BufferOffset)
|
||||
EndProcedure
|
||||
|
||||
|
||||
;-> Macros
|
||||
|
||||
; Same as `MsgPackWriteArray()` and `MsgPackWriteMap()`, but force the given format code.
|
||||
|
||||
Macro MsgPackWriteFixArray(MsgPackData, Buffer, BufferSize)
|
||||
_MsgPackWriteContainer(MsgPackData, #False, 0, Buffer, BufferSize)
|
||||
EndMacro
|
||||
|
||||
Macro MsgPackWriteArray16(MsgPackData, Buffer, BufferSize)
|
||||
_MsgPackWriteContainer(MsgPackData, #False, 2, Buffer, BufferSize)
|
||||
EndMacro
|
||||
|
||||
Macro MsgPackWriteArray32(MsgPackData, Buffer, BufferSize)
|
||||
_MsgPackWriteContainer(MsgPackData, #False, 4, Buffer, BufferSize)
|
||||
EndMacro
|
||||
|
||||
Macro MsgPackWriteFixMap(MsgPackData, Buffer, BufferSize)
|
||||
_MsgPackWriteContainer(MsgPackData, #True, 0, Buffer, BufferSize)
|
||||
EndMacro
|
||||
|
||||
Macro MsgPackWriteMap16(MsgPackData, Buffer, BufferSize)
|
||||
_MsgPackWriteContainer(MsgPackData, #True, 2, Buffer, BufferSize)
|
||||
EndMacro
|
||||
|
||||
Macro MsgPackWriteMap32(MsgPackData, Buffer, BufferSize)
|
||||
_MsgPackWriteContainer(MsgPackData, #True, 4, Buffer, BufferSize)
|
||||
EndMacro
|
||||
|
||||
+40
-26
@@ -25,6 +25,7 @@ CompilerEndIf
|
||||
|
||||
XIncludeFile "./_Commons.pbi"
|
||||
XIncludeFile "./_Allocators.pbi"
|
||||
XIncludeFile "./_Primitives.pbi"
|
||||
|
||||
|
||||
|
||||
@@ -33,20 +34,25 @@ XIncludeFile "./_Allocators.pbi"
|
||||
|
||||
;-> Getters
|
||||
|
||||
;Procedure.i MsgPackReadNull(*MsgPackData.MsgPackData)
|
||||
; CompilerIf Not #MsgPack_DisableNullChecks
|
||||
; If Not *MsgPackData
|
||||
; DebuggerError("A #Null MsgPackData pointer was passed !")
|
||||
; ProcedureReturn #Null
|
||||
; EndIf
|
||||
; CompilerEndIf
|
||||
;EndProcedure
|
||||
; Reads a nil from the given data.
|
||||
; Returns `#MsgPack_Error_Success` on success, something else otherwise.
|
||||
Procedure.MsgPack_ErrorCode MsgPackReadNull(*MsgPackData.MsgPackData)
|
||||
Protected ErrorCode.MsgPack_ErrorCode
|
||||
|
||||
ErrorCode = _MsgPackExpectRead(*MsgPackData, #MsgPack_FormatCode_Null, 1)
|
||||
If ErrorCode <> #MsgPack_Error_Success
|
||||
ProcedureReturn ErrorCode
|
||||
EndIf
|
||||
|
||||
_MsgPackMoveOffsetAfterRead(*MsgPackData, 1)
|
||||
|
||||
ProcedureReturn #MsgPack_Error_Success
|
||||
EndProcedure
|
||||
|
||||
|
||||
; Reads a boolean from the given data.
|
||||
; Returns: The boolean value `#True` or `#False` as a `.b`
|
||||
; OnError: Returns `#False` and sets the `MsgPackData\LastError` field.
|
||||
; Will also raise a `DebuggerError`.
|
||||
Procedure.b MsgPackReadBoolean(*MsgPackData.MsgPackData)
|
||||
CompilerIf Not #MsgPack_DisableNullChecks
|
||||
If Not *MsgPackData
|
||||
@@ -58,7 +64,7 @@ Procedure.b MsgPackReadBoolean(*MsgPackData.MsgPackData)
|
||||
*MsgPackData\LastError = #MsgPack_Error_Success
|
||||
|
||||
If _MsgPackIsAtTheEnd(*MsgPackData)
|
||||
DebuggerError("End of buffer reached, cannot check data format code !")
|
||||
Debug("End of buffer reached, cannot check data format code !")
|
||||
|
||||
*MsgPackData\LastError = #MsgPack_Error_AtOrPastTheEndOfBuffer
|
||||
ProcedureReturn #False
|
||||
@@ -79,10 +85,10 @@ Procedure.b MsgPackReadBoolean(*MsgPackData.MsgPackData)
|
||||
|
||||
ProcedureReturn #True
|
||||
Else
|
||||
DebuggerError("The format code isn't the one for a boolean ! (" +
|
||||
RSet(Hex(PeekA(*MsgPackData\Buffer + *MsgPackData\BufferOffset)), 2, "0") +
|
||||
" vs "+ RSet(Hex(#MsgPack_FormatCode_False), 2, "0") +
|
||||
"/"+ RSet(Hex(#MsgPack_FormatCode_True), 2, "0") + ")")
|
||||
Debug("The format code isn't the one for a boolean ! (" +
|
||||
RSet(Hex(PeekA(*MsgPackData\Buffer + *MsgPackData\BufferOffset)), 2, "0") +
|
||||
" vs "+ RSet(Hex(#MsgPack_FormatCode_False), 2, "0") +
|
||||
"/"+ RSet(Hex(#MsgPack_FormatCode_True), 2, "0") + ")")
|
||||
|
||||
*MsgPackData\LastError = #MsgPack_Error_InvalidFormatCode
|
||||
ProcedureReturn #False
|
||||
@@ -102,8 +108,14 @@ Procedure.MsgPack_ErrorCode _MsgPackWriteLiteral(*MsgPackData.MsgPackData, Liter
|
||||
EndIf
|
||||
CompilerEndIf
|
||||
|
||||
If _MsgPackHasFlag(*MsgPackData, #MsgPack_DataFlags_IsReadOnly)
|
||||
Debug("Cannot write in a read-only buffer !")
|
||||
*MsgPackData\LastError = #MsgPack_Error_BufferIsReadOnly
|
||||
ProcedureReturn #MsgPack_Error_BufferIsReadOnly
|
||||
EndIf
|
||||
|
||||
*MsgPackData\LastError = #MsgPack_Error_Success
|
||||
|
||||
|
||||
If Not _MsgPackHasSpaceLeft(*MsgPackData, 1)
|
||||
Protected GrowthErrorCode.MsgPack_ErrorCode
|
||||
|
||||
@@ -126,17 +138,6 @@ Procedure.MsgPack_ErrorCode _MsgPackWriteLiteral(*MsgPackData.MsgPackData, Liter
|
||||
EndProcedure
|
||||
|
||||
|
||||
;-> > Utility
|
||||
|
||||
; Procedure.b MsgPackWriteBoolean(*MsgPackData.MsgPackData, Boolean)
|
||||
; If Boolean
|
||||
; ProcedureReturn MsgPackWriteTrue(*MsgPackData)
|
||||
; Else
|
||||
; ProcedureReturn MsgPackWriteFalse(*MsgPackData)
|
||||
; EndIf
|
||||
; EndProcedure
|
||||
|
||||
|
||||
;-> Macro aliases
|
||||
|
||||
; @Function MsgPackWriteNull(*Ptr.MsgPackData).MsgPack_ErrorCode
|
||||
@@ -155,3 +156,16 @@ Macro MsgPackWriteNull(MsgPackData) : _MsgPackWriteLiteral(MsgPackData, #MsgPack
|
||||
Macro MsgPackWriteTrue(MsgPackData) : _MsgPackWriteLiteral(MsgPackData, #MsgPack_FormatCode_True) : EndMacro
|
||||
|
||||
Macro MsgPackWriteFalse(MsgPackData) : _MsgPackWriteLiteral(MsgPackData, #MsgPack_FormatCode_False) : EndMacro
|
||||
|
||||
|
||||
;-> > Utility
|
||||
|
||||
; Writes `#True` or `#False` depending on `Boolean`.
|
||||
; Returns `#MsgPack_Error_Success` on success, something else otherwise.
|
||||
Procedure.MsgPack_ErrorCode MsgPackWriteBoolean(*MsgPackData.MsgPackData, Boolean.b)
|
||||
If Boolean
|
||||
ProcedureReturn MsgPackWriteTrue(*MsgPackData)
|
||||
EndIf
|
||||
|
||||
ProcedureReturn MsgPackWriteFalse(*MsgPackData)
|
||||
EndProcedure
|
||||
|
||||
@@ -51,6 +51,12 @@ Procedure.MsgPack_ErrorCode _MsgPackReserveWrite(*MsgPackData.MsgPackData, Size.
|
||||
|
||||
; `Size` is a quad so sized fields up to 4 GiB can't be truncated, but the end
|
||||
; offset must still fit in a `.i`, which isn't the case on x86 past 2 GiB.
|
||||
If _MsgPackHasFlag(*MsgPackData, #MsgPack_DataFlags_IsReadOnly)
|
||||
Debug("Cannot write in a read-only buffer !")
|
||||
*MsgPackData\LastError = #MsgPack_Error_BufferIsReadOnly
|
||||
ProcedureReturn #MsgPack_Error_BufferIsReadOnly
|
||||
EndIf
|
||||
|
||||
If Size < 0 Or Size > #_MsgPack_MaxIntegerValue - *MsgPackData\BufferOffset
|
||||
DebuggerWarning("Requested size can't be addressed: " + Str(Size))
|
||||
*MsgPackData\LastError = #MsgPack_Error_BinaryDataSizeIsTooBig
|
||||
@@ -143,6 +149,7 @@ Procedure.MsgPack_ErrorCode _MsgPackWriteFixedField(*MsgPackData.MsgPackData, Fo
|
||||
PokeQ(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1, EndianSwapI64(Value))
|
||||
EndSelect
|
||||
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + 1 + ValueByteLength
|
||||
_MsgPackRewindOffsetAfterWrite(*MsgPackData, 1 + ValueByteLength)
|
||||
|
||||
ProcedureReturn #MsgPack_Error_Success
|
||||
EndProcedure
|
||||
@@ -169,7 +176,7 @@ Procedure.q _MsgPackReadFixedField(*MsgPackData.MsgPackData, FormatCode.MsgPack_
|
||||
Case 8
|
||||
ReturnedValue = EndianSwapI64(PeekQ(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1))
|
||||
EndSelect
|
||||
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + 1 + ValueByteLength
|
||||
_MsgPackMoveOffsetAfterRead(*MsgPackData, 1 + ValueByteLength)
|
||||
|
||||
ProcedureReturn ReturnedValue
|
||||
EndProcedure
|
||||
@@ -353,7 +360,7 @@ Procedure.q MsgPackReadInteger(*MsgPackData.MsgPackData)
|
||||
#MsgPack_FormatCode_FixIntNegative:
|
||||
; The whole byte is the value, both ranges are valid signed bytes.
|
||||
ReturnedValue = PeekB(*MsgPackData\Buffer + *MsgPackData\BufferOffset)
|
||||
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + 1
|
||||
_MsgPackMoveOffsetAfterRead(*MsgPackData, 1)
|
||||
*MsgPackData\LastError = #MsgPack_Error_Success
|
||||
ProcedureReturn ReturnedValue
|
||||
Case #MsgPack_FormatCode_UInt8:
|
||||
|
||||
@@ -86,6 +86,7 @@ Procedure.MsgPack_ErrorCode _MsgPackWriteStringField(*MsgPackData.MsgPackData, F
|
||||
PokeS(*MsgPackData\Buffer + *MsgPackData\BufferOffset, String, -1, Encoding | #PB_String_NoZero)
|
||||
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + StringSize
|
||||
EndIf
|
||||
_MsgPackRewindOffsetAfterWrite(*MsgPackData, 1 + SizeFieldByteLength + StringSize)
|
||||
|
||||
ProcedureReturn #MsgPack_Error_Success
|
||||
EndProcedure
|
||||
@@ -128,7 +129,7 @@ Procedure.s MsgPackReadString(*MsgPackData.MsgPackData, Encoding.i = #PB_UTF8)
|
||||
EndIf
|
||||
|
||||
If Encoding = #PB_Unicode And StringSize % 2 <> 0
|
||||
DebuggerWarning("UTF-16 string has an odd byte size !")
|
||||
Debug("UTF-16 string has an odd byte size !")
|
||||
*MsgPackData\LastError = #MsgPack_Error_InvalidStringSize
|
||||
ProcedureReturn ""
|
||||
EndIf
|
||||
@@ -146,7 +147,7 @@ Procedure.s MsgPackReadString(*MsgPackData.MsgPackData, Encoding.i = #PB_UTF8)
|
||||
EndSelect
|
||||
EndIf
|
||||
|
||||
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + HeaderSize + StringSize
|
||||
_MsgPackMoveOffsetAfterRead(*MsgPackData, HeaderSize + StringSize)
|
||||
|
||||
ProcedureReturn ReturnedValue
|
||||
EndProcedure
|
||||
|
||||
+6
-3
@@ -42,7 +42,7 @@ Procedure.MsgPack_FormatCode MsgPackGetFormatCode(*MsgPackData.MsgPackData)
|
||||
EndIf
|
||||
|
||||
If _MsgPackIsAtTheEnd(*MsgPackData)
|
||||
DebuggerError("End of buffer reached, cannot check data format code !")
|
||||
Debug("End of buffer reached, cannot check data format code !")
|
||||
ProcedureReturn #MsgPack_FormatCode_Invalid
|
||||
EndIf
|
||||
|
||||
@@ -131,9 +131,12 @@ Procedure.MsgPack_DataType MsgPackFormatCodeToDataType(FormatCode.MsgPack_Format
|
||||
ProcedureReturn #MsgPack_DataTypes_Extension
|
||||
|
||||
Case #MsgPack_FormatCode_Invalid:
|
||||
; Can come from the data itself, or `MsgPackGetFormatCode()` at the end of the buffer.
|
||||
ProcedureReturn #MsgPack_DataTypes_Unknown
|
||||
|
||||
Default:
|
||||
; FIXME: ADD MESSAGE HERE !!!
|
||||
DebuggerWarning("")
|
||||
; `MsgPackGetFormatCode()` never returns these, the caller passed an invalid value.
|
||||
DebuggerWarning("Unknown format code: 0x" + RSet(Hex(FormatCode), 2, "0"))
|
||||
ProcedureReturn #MsgPack_DataTypes_Unknown
|
||||
EndSelect
|
||||
EndProcedure
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
EnableExplicit
|
||||
|
||||
XIncludeFile "../_UnitTesting.pbi"
|
||||
XIncludeFile "../../Includes/MessagePack.pbi"
|
||||
XIncludeFile "../_TestBuffers.pbi"
|
||||
|
||||
|
||||
; Checks that `#MsgPack_DataFlags_MoveOffsetAfterRead/Write` are honored by every family,
|
||||
; and that `#MsgPack_DataFlags_IsReadOnly` blocks every write.
|
||||
|
||||
|
||||
Define *MsgPack.MsgPackData
|
||||
Define *Iterator.MsgPackData
|
||||
Define *Output = AllocateMemory(16)
|
||||
Define Bytes.l = $44332211
|
||||
|
||||
|
||||
; Writes without moving the offset, each one overwriting the previous one
|
||||
*MsgPack = MsgPackCreate(32)
|
||||
*MsgPack\DataFlags = *MsgPack\DataFlags & (~#MsgPack_DataFlags_MoveOffsetAfterWrite)
|
||||
|
||||
AssertEquals(MsgPackWriteInteger(*MsgPack, 1000), #MsgPack_Error_Success, "Wrote an int without moving")
|
||||
AssertEquals(*MsgPack\BufferOffset, 0, "Int write didn't move the offset")
|
||||
AssertEquals(MsgPackWriteString(*MsgPack, "abc"), #MsgPack_Error_Success, "Wrote a string without moving")
|
||||
AssertEquals(*MsgPack\BufferOffset, 0, "String write didn't move the offset")
|
||||
AssertEquals(MsgPackWriteBinary(*MsgPack, @Bytes, 4), #MsgPack_Error_Success, "Wrote a bin without moving")
|
||||
AssertEquals(*MsgPack\BufferOffset, 0, "Bin write didn't move the offset")
|
||||
AssertEquals(MsgPackWriteExtension(*MsgPack, 1, @Bytes, 4), #MsgPack_Error_Success, "Wrote a fixext without moving")
|
||||
AssertEquals(*MsgPack\BufferOffset, 0, "Fixext write didn't move the offset")
|
||||
AssertEquals(MsgPackWriteExt8(*MsgPack, 1, @Bytes, 3), #MsgPack_Error_Success, "Wrote an ext8 without moving")
|
||||
AssertEquals(*MsgPack\BufferOffset, 0, "Ext8 write didn't move the offset")
|
||||
AssertEquals(MsgPackWriteArray(*MsgPack, @Bytes, 2), #MsgPack_Error_Success, "Wrote an array without moving")
|
||||
AssertEquals(*MsgPack\BufferOffset, 0, "Array write didn't move the offset")
|
||||
AssertEqualsA(PeekA(*MsgPack\Buffer), $92, "Array was written at the start")
|
||||
|
||||
MsgPackFree(*MsgPack, #True)
|
||||
|
||||
|
||||
; Reads without moving the offset, so the same field can be read twice
|
||||
*MsgPack = WrapHex("CD03E8A3616263C4021122D401AAC0")
|
||||
*MsgPack\DataFlags = *MsgPack\DataFlags & (~#MsgPack_DataFlags_MoveOffsetAfterRead)
|
||||
|
||||
AssertEqualsQ(MsgPackReadInteger(*MsgPack), 1000, "Peeked an int")
|
||||
AssertEqualsQ(MsgPackReadUInt16(*MsgPack), 1000, "Read the same int again")
|
||||
AssertEquals(*MsgPack\BufferOffset, 0, "Int reads didn't move the offset")
|
||||
MsgPackSkip(*MsgPack)
|
||||
|
||||
AssertIsTrue(Bool(MsgPackReadString(*MsgPack) = "abc"), "Peeked a string")
|
||||
AssertEquals(*MsgPack\BufferOffset, 3, "String read didn't move the offset")
|
||||
MsgPackSkip(*MsgPack)
|
||||
|
||||
AssertEqualsQ(MsgPackReadBinary(*MsgPack, *Output, 16), 2, "Peeked a bin")
|
||||
AssertEquals(*MsgPack\BufferOffset, 7, "Bin read didn't move the offset")
|
||||
MsgPackSkip(*MsgPack)
|
||||
|
||||
AssertEqualsQ(MsgPackReadExtension(*MsgPack, *Output, 16), 1, "Peeked an ext")
|
||||
AssertEquals(*MsgPack\BufferOffset, 11, "Ext read didn't move the offset")
|
||||
MsgPackSkip(*MsgPack)
|
||||
|
||||
AssertEquals(MsgPackReadNull(*MsgPack), #MsgPack_Error_Success, "Peeked a nil")
|
||||
AssertEquals(*MsgPack\BufferOffset, 14, "Nil read didn't move the offset")
|
||||
|
||||
FreeWrappedHex(*MsgPack)
|
||||
|
||||
|
||||
; Views are read-only: [1, 2]
|
||||
*MsgPack = WrapHex("920102")
|
||||
*Iterator = MsgPackExamine(*MsgPack)
|
||||
MsgPackNextEntry(*Iterator)
|
||||
|
||||
AssertEquals(MsgPackWriteNull(*Iterator), #MsgPack_Error_BufferIsReadOnly, "Cannot write a literal in a view")
|
||||
AssertEquals(MsgPackWriteBoolean(*Iterator, #True), #MsgPack_Error_BufferIsReadOnly, "Cannot write a boolean in a view")
|
||||
AssertEquals(MsgPackWriteInteger(*Iterator, 5), #MsgPack_Error_BufferIsReadOnly, "Cannot write an int in a view")
|
||||
AssertEquals(MsgPackWriteString(*Iterator, "a"), #MsgPack_Error_BufferIsReadOnly, "Cannot write a string in a view")
|
||||
AssertEquals(MsgPackWriteBinary(*Iterator, @Bytes, 1), #MsgPack_Error_BufferIsReadOnly, "Cannot write a bin in a view")
|
||||
AssertEquals(MsgPackWriteExtension(*Iterator, 1, @Bytes, 1), #MsgPack_Error_BufferIsReadOnly, "Cannot write an ext in a view")
|
||||
AssertEquals(MsgPackWriteArray(*Iterator, @Bytes, 1), #MsgPack_Error_BufferIsReadOnly, "Cannot write an array in a view")
|
||||
AssertEquals(MsgPackLastError(*Iterator), #MsgPack_Error_BufferIsReadOnly, "Read-only error is set")
|
||||
AssertEquals(*Iterator\BufferOffset, 0, "Offset is untouched by the rejected writes")
|
||||
AssertIsTrue(Bool(PeekA(*MsgPack\Buffer + 1) = $01 And PeekA(*MsgPack\Buffer + 2) = $02), "Parent data is untouched")
|
||||
|
||||
AssertEqualsQ(MsgPackReadInteger(*Iterator), 1, "View can still be read")
|
||||
MsgPackFinish(*Iterator)
|
||||
FreeWrappedHex(*MsgPack)
|
||||
|
||||
|
||||
; Nil and booleans
|
||||
*MsgPack = MsgPackCreate(1)
|
||||
AssertEquals(MsgPackWriteBoolean(*MsgPack, #True), #MsgPack_Error_Success, "Wrote a true boolean")
|
||||
AssertEquals(MsgPackWriteBoolean(*MsgPack, #False), #MsgPack_Error_Success, "Wrote a false boolean")
|
||||
AssertEquals(MsgPackWriteNull(*MsgPack), #MsgPack_Error_Success, "Wrote a nil")
|
||||
*MsgPack\BufferOffset = 0
|
||||
|
||||
AssertEquals(MsgPackReadNull(*MsgPack), #MsgPack_Error_InvalidFormatCode, "True isn't a nil")
|
||||
AssertEquals(*MsgPack\BufferOffset, 0, "Failed nil read didn't move the offset")
|
||||
AssertIsTrue(MsgPackReadBoolean(*MsgPack), "Read the true boolean")
|
||||
AssertIsFalse(MsgPackReadBoolean(*MsgPack), "Read the false boolean")
|
||||
AssertIsFalse(MsgPackReadBoolean(*MsgPack), "Nil isn't a boolean")
|
||||
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_InvalidFormatCode, "Boolean error is set")
|
||||
AssertEquals(MsgPackReadNull(*MsgPack), #MsgPack_Error_Success, "Read the nil")
|
||||
AssertEquals(MsgPackReadNull(*MsgPack), #MsgPack_Error_AtOrPastTheEndOfBuffer, "Cannot read a nil at the end")
|
||||
AssertEquals(MsgPackGetFormatCode(*MsgPack), #MsgPack_FormatCode_Invalid, "No format code at the end")
|
||||
AssertEquals(MsgPackFormatCodeToDataType(MsgPackGetFormatCode(*MsgPack)), #MsgPack_DataTypes_Unknown, "No data type at the end")
|
||||
MsgPackFree(*MsgPack, #True)
|
||||
|
||||
|
||||
FreeMemory(*Output)
|
||||
|
||||
|
||||
|
||||
If FailedUnitTests
|
||||
End 1
|
||||
EndIf
|
||||
|
||||
Debug "Done !"
|
||||
|
||||
End 0
|
||||
@@ -28,9 +28,9 @@ Procedure TestValueMetaInfoWithin(CodeForBuffer.a, ExpectedVariableSize.q, Expec
|
||||
EndProcedure
|
||||
|
||||
|
||||
; Positive FixInt
|
||||
TestValueMetaInfoWithin($00, 0, 1, "FixInt with positive 0") ; Value: 0 (0x00) | Offset: ???
|
||||
TestValueMetaInfoWithin($7F, 127, 0, "FixInt with positive 127") ; Value: 127 (0x7F) | Offset: ???
|
||||
; Positive FixInt, unsupported since `MsgPackReadInteger()` handles them
|
||||
TestValueMetaInfoWithin($00, #MsgPack_InvalidSize, #MsgPack_InvalidSize, "FixInt with positive 0")
|
||||
TestValueMetaInfoWithin($7F, #MsgPack_InvalidSize, #MsgPack_InvalidSize, "FixInt with positive 127")
|
||||
|
||||
; FixMap
|
||||
TestValueMetaInfoWithin($80, 0, 1, "FixMap with 0 elements") ; Value: 0 (0x0) | Offset: 1
|
||||
@@ -46,9 +46,9 @@ TestValueMetaInfoWithin($AF, 15, 1, "FixStr with 15 elements") ; Value: 15 (0x0F
|
||||
TestValueMetaInfoWithin($B0, 16, 1, "FixStr with 16 elements") ; Value: 16 (0x10) | Offset: 1
|
||||
TestValueMetaInfoWithin($BF, 31, 1, "FixStr with 31 elements") ; Value: 31 (0x1F) | Offset: 1
|
||||
|
||||
; Negative FixInt
|
||||
TestValueMetaInfoWithin($E0, -0, 1, "FixInt with negative 0") ; Value: 0 (0x00) | Offset: ???
|
||||
TestValueMetaInfoWithin($FF, -31, 0, "FixInt with negative 31") ; Value: 31 (0x7F) | Offset: ???
|
||||
; Negative FixInt, unsupported since `MsgPackReadInteger()` handles them
|
||||
TestValueMetaInfoWithin($E0, #MsgPack_InvalidSize, #MsgPack_InvalidSize, "FixInt with negative 32")
|
||||
TestValueMetaInfoWithin($FF, #MsgPack_InvalidSize, #MsgPack_InvalidSize, "FixInt with negative 1")
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
EnableExplicit
|
||||
|
||||
XIncludeFile "../_UnitTesting.pbi"
|
||||
XIncludeFile "../../Includes/MessagePack.pbi"
|
||||
XIncludeFile "../_TestBuffers.pbi"
|
||||
|
||||
|
||||
; Note: Only covers errors that don't call `DebuggerError()`.
|
||||
|
||||
|
||||
Procedure.s WrittenHex(*MsgPack.MsgPackData)
|
||||
Protected Result.s = ""
|
||||
Protected i.i
|
||||
|
||||
For i = 0 To *MsgPack\BufferOffset - 1
|
||||
Result = Result + RSet(Hex(PeekA(*MsgPack\Buffer + i)), 2, "0")
|
||||
Next
|
||||
|
||||
ProcedureReturn Result
|
||||
EndProcedure
|
||||
|
||||
|
||||
; Writes the content given as hex with `WriteMode` and checks the whole output.
|
||||
; Modes: 0 = Array, 1 = Map, 2 = FixArray, 3 = Array16, 4 = Array32, 5 = FixMap, 6 = Map16, 7 = Map32
|
||||
Procedure TestWrite(WriteMode.i, ContentHex.s, ExpectedError.i, ExpectedHex.s, TestName.s)
|
||||
Protected *Content.MsgPackData = WrapHex(ContentHex + "00")
|
||||
Protected *MsgPack.MsgPackData = MsgPackCreate(1)
|
||||
Protected ContentSize.q = Len(ContentHex) / 2
|
||||
Protected ErrorCode.i
|
||||
|
||||
Select WriteMode
|
||||
Case 0 : ErrorCode = MsgPackWriteArray(*MsgPack, *Content\Buffer, ContentSize)
|
||||
Case 1 : ErrorCode = MsgPackWriteMap(*MsgPack, *Content\Buffer, ContentSize)
|
||||
Case 2 : ErrorCode = MsgPackWriteFixArray(*MsgPack, *Content\Buffer, ContentSize)
|
||||
Case 3 : ErrorCode = MsgPackWriteArray16(*MsgPack, *Content\Buffer, ContentSize)
|
||||
Case 4 : ErrorCode = MsgPackWriteArray32(*MsgPack, *Content\Buffer, ContentSize)
|
||||
Case 5 : ErrorCode = MsgPackWriteFixMap(*MsgPack, *Content\Buffer, ContentSize)
|
||||
Case 6 : ErrorCode = MsgPackWriteMap16(*MsgPack, *Content\Buffer, ContentSize)
|
||||
Case 7 : ErrorCode = MsgPackWriteMap32(*MsgPack, *Content\Buffer, ContentSize)
|
||||
EndSelect
|
||||
|
||||
AssertEquals(ErrorCode, ExpectedError, TestName + " (Error)")
|
||||
AssertEquals(MsgPackLastError(*MsgPack), ExpectedError, TestName + " (Last error)")
|
||||
AssertIsTrue(Bool(WrittenHex(*MsgPack) = ExpectedHex), TestName + " (Output)", WrittenHex(*MsgPack) + " <> " + ExpectedHex)
|
||||
|
||||
MsgPackFree(*MsgPack, #True)
|
||||
FreeWrappedHex(*Content)
|
||||
EndProcedure
|
||||
|
||||
|
||||
Define *MsgPack.MsgPackData
|
||||
Define *Inner.MsgPackData
|
||||
Define *Iterator.MsgPackData
|
||||
Define *Finalized
|
||||
Define Content.s
|
||||
Define i.i
|
||||
|
||||
|
||||
; Smallest fit
|
||||
TestWrite(0, "", #MsgPack_Error_Success, "90", "Empty array")
|
||||
TestWrite(1, "", #MsgPack_Error_Success, "80", "Empty map")
|
||||
TestWrite(0, "01A16192C0C3", #MsgPack_Error_Success, "9301A16192C0C3", "FixArray with a nested array")
|
||||
TestWrite(1, "A16101A16292C0C3", #MsgPack_Error_Success, "82A16101A16292C0C3", "FixMap with a nested array")
|
||||
|
||||
Content = ""
|
||||
For i = 1 To 16
|
||||
Content = Content + "C0"
|
||||
Next
|
||||
TestWrite(0, Content, #MsgPack_Error_Success, "DC0010" + Content, "Array16 for 16 entries")
|
||||
TestWrite(1, Content, #MsgPack_Error_Success, "88" + Content, "FixMap for 8 pairs")
|
||||
|
||||
Content = ""
|
||||
For i = 1 To 32
|
||||
Content = Content + "C0"
|
||||
Next
|
||||
TestWrite(1, Content, #MsgPack_Error_Success, "DE0010" + Content, "Map16 for 16 pairs")
|
||||
|
||||
; Forced format codes
|
||||
TestWrite(2, "01", #MsgPack_Error_Success, "9101", "Forced FixArray")
|
||||
TestWrite(3, "01", #MsgPack_Error_Success, "DC000101", "Forced Array16")
|
||||
TestWrite(4, "01", #MsgPack_Error_Success, "DD0000000101", "Forced Array32")
|
||||
TestWrite(5, "0102", #MsgPack_Error_Success, "810102", "Forced FixMap")
|
||||
TestWrite(6, "0102", #MsgPack_Error_Success, "DE00010102", "Forced Map16")
|
||||
TestWrite(7, "0102", #MsgPack_Error_Success, "DF000000010102", "Forced Map32")
|
||||
|
||||
; Errors, nothing must be written
|
||||
TestWrite(1, "A16101A162", #MsgPack_Error_OddMapFieldCount, "", "Map with a key lacking its value")
|
||||
TestWrite(5, "01", #MsgPack_Error_OddMapFieldCount, "", "FixMap with a single field")
|
||||
TestWrite(2, Content, #MsgPack_Error_TooManyEntries, "", "FixArray with 32 entries")
|
||||
TestWrite(0, "01C1", #MsgPack_Error_InvalidFormatCode, "", "Array with invalid content")
|
||||
TestWrite(0, "01A361", #MsgPack_Error_BufferTooSmall, "", "Array with a truncated field")
|
||||
TestWrite(0, "9201", #MsgPack_Error_BufferTooSmall, "", "Array with a truncated nested array")
|
||||
|
||||
|
||||
; From a `MsgPackData`, and from a finalized buffer, then read back
|
||||
*Inner = MsgPackCreate(4)
|
||||
MsgPackWriteString(*Inner, "id")
|
||||
MsgPackWriteInteger(*Inner, 1234)
|
||||
MsgPackWriteString(*Inner, "tags")
|
||||
|
||||
*MsgPack = MsgPackCreate(1)
|
||||
AssertEquals(MsgPackWriteMapFrom(*MsgPack, *Inner), #MsgPack_Error_OddMapFieldCount, "Inner map content lacks a value")
|
||||
AssertEquals(*MsgPack\BufferOffset, 0, "Nothing was written for the odd map")
|
||||
|
||||
MsgPackWriteArray(*Inner, ?Tags, ?TagsEnd - ?Tags)
|
||||
AssertEquals(MsgPackWriteMapFrom(*MsgPack, *Inner), #MsgPack_Error_Success, "Wrote the map from a MsgPackData")
|
||||
AssertEquals(MsgPackWriteArrayFrom(*MsgPack, *Inner), #MsgPack_Error_Success, "Wrote an array from the same MsgPackData")
|
||||
|
||||
*Finalized = MsgPackFinalize(*Inner)
|
||||
AssertEquals(MsgPackWriteArray(*MsgPack, *Finalized, MemorySize(*Finalized)), #MsgPack_Error_Success, "Wrote an array from a finalized buffer")
|
||||
FreeMemory(*Finalized)
|
||||
|
||||
AssertIsTrue(Bool(WrittenHex(*MsgPack) = "82A26964CD04D2A47461677392A161A162" + "94A26964CD04D2A47461677392A161A162" + "94A26964CD04D2A47461677392A161A162"), "Written buffer", WrittenHex(*MsgPack))
|
||||
|
||||
*MsgPack\BufferOffset = 0
|
||||
*Iterator = MsgPackExamine(*MsgPack)
|
||||
AssertIsTrue(MsgPackNextEntry(*Iterator), "Read back entry #1")
|
||||
AssertIsTrue(Bool(MsgPackReadString(*Iterator) = "id"), "Read back key #1")
|
||||
AssertEqualsQ(MsgPackReadInteger(*Iterator), 1234, "Read back value #1")
|
||||
AssertIsTrue(MsgPackNextEntry(*Iterator), "Read back entry #2")
|
||||
AssertIsTrue(Bool(MsgPackReadString(*Iterator) = "tags"), "Read back key #2")
|
||||
AssertEqualsQ(MsgPackGetEntryCount(*Iterator), 2, "Read back value #2 entry count")
|
||||
AssertIsFalse(MsgPackNextEntry(*Iterator), "Read back map has 2 entries")
|
||||
MsgPackFinish(*Iterator)
|
||||
|
||||
AssertEqualsQ(MsgPackGetEntryCount(*MsgPack), 4, "Read back array has 4 entries")
|
||||
|
||||
|
||||
MsgPackFree(*MsgPack, #True)
|
||||
|
||||
|
||||
|
||||
If FailedUnitTests
|
||||
End 1
|
||||
EndIf
|
||||
|
||||
Debug "Done !"
|
||||
|
||||
End 0
|
||||
|
||||
|
||||
DataSection
|
||||
Tags:
|
||||
Data.a $A1, $61, $A1, $62
|
||||
TagsEnd:
|
||||
EndDataSection
|
||||
@@ -0,0 +1,253 @@
|
||||
EnableExplicit
|
||||
|
||||
XIncludeFile "../_UnitTesting.pbi"
|
||||
XIncludeFile "../../Includes/MessagePack.pbi"
|
||||
XIncludeFile "../_TestBuffers.pbi"
|
||||
|
||||
|
||||
; Note: Only covers errors that don't call `DebuggerError()`.
|
||||
|
||||
|
||||
; Setup
|
||||
Define *MsgPack.MsgPackData
|
||||
Define *Iterator.MsgPackData
|
||||
Define *Nested.MsgPackData
|
||||
Define EntryCount.i
|
||||
Define i.i
|
||||
Define Hex.s
|
||||
|
||||
|
||||
Procedure TestFullSize(Hex.s, ExpectedSize.q, TestName.s)
|
||||
Protected *MsgPack.MsgPackData = WrapHex(Hex)
|
||||
|
||||
AssertEqualsQ(MsgPackGetFieldFullSize(*MsgPack), ExpectedSize, TestName + " (Size)")
|
||||
AssertEquals(*MsgPack\BufferOffset, 0, TestName + " (Offset untouched)")
|
||||
|
||||
FreeWrappedHex(*MsgPack)
|
||||
EndProcedure
|
||||
|
||||
|
||||
; Full sizes of single fields, with an extra trailing byte that must not be counted
|
||||
TestFullSize("C0FF", 1, "Nil")
|
||||
TestFullSize("C3FF", 1, "True")
|
||||
TestFullSize("7FFF", 1, "Positive fixint")
|
||||
TestFullSize("E0FF", 1, "Negative fixint")
|
||||
TestFullSize("CCFFFF", 2, "UInt8")
|
||||
TestFullSize("D10001FF", 3, "Int16")
|
||||
TestFullSize("CE00000001FF", 5, "UInt32")
|
||||
TestFullSize("CA3FC00000FF", 5, "Float32")
|
||||
TestFullSize("D30000000000000001FF", 9, "Int64")
|
||||
TestFullSize("CB4000000000000000FF", 9, "Float64")
|
||||
TestFullSize("A26162FF", 3, "FixStr")
|
||||
TestFullSize("D9026162FF", 4, "Str8")
|
||||
TestFullSize("C50002AAAAFF", 5, "Bin16")
|
||||
TestFullSize("D401AAFF", 3, "FixExt1")
|
||||
TestFullSize("D801AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFF", 18, "FixExt16")
|
||||
TestFullSize("C70205AAAAFF", 5, "Ext8")
|
||||
TestFullSize("90FF", 1, "Empty array")
|
||||
TestFullSize("80FF", 1, "Empty map")
|
||||
TestFullSize("920102FF", 3, "FixArray")
|
||||
TestFullSize("DC00020102FF", 5, "Array16")
|
||||
TestFullSize("DD000000020102FF", 7, "Array32")
|
||||
TestFullSize("82A16101A16202FF", 7, "FixMap")
|
||||
TestFullSize("DE0001A16101FF", 6, "Map16")
|
||||
TestFullSize("9291C091C0FF", 5, "Nested arrays")
|
||||
|
||||
; Invalid and truncated fields
|
||||
*MsgPack = WrapHex("C1")
|
||||
AssertEqualsQ(MsgPackGetFieldFullSize(*MsgPack), #MsgPack_InvalidSize, "Invalid format code has no size")
|
||||
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_InvalidFormatCode, "Invalid format code error is set")
|
||||
FreeWrappedHex(*MsgPack)
|
||||
|
||||
*MsgPack = WrapHex("930102")
|
||||
AssertEqualsQ(MsgPackGetFieldFullSize(*MsgPack), #MsgPack_InvalidSize, "Truncated array has no size")
|
||||
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_BufferTooSmall, "Truncated array error is set")
|
||||
AssertEquals(*MsgPack\BufferOffset, 0, "Offset is untouched by the truncated array")
|
||||
FreeWrappedHex(*MsgPack)
|
||||
|
||||
*MsgPack = WrapHex("DDFFFFFFFF01")
|
||||
AssertEqualsQ(MsgPackGetFieldFullSize(*MsgPack), #MsgPack_InvalidSize, "Bogus array size is rejected")
|
||||
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_BufferTooSmall, "Bogus array size error is set")
|
||||
FreeWrappedHex(*MsgPack)
|
||||
|
||||
*MsgPack = WrapHex("CD00")
|
||||
AssertEqualsQ(MsgPackGetFieldFullSize(*MsgPack), #MsgPack_InvalidSize, "Truncated UInt16 has no size")
|
||||
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_BufferTooSmall, "Truncated UInt16 error is set")
|
||||
FreeWrappedHex(*MsgPack)
|
||||
|
||||
; Deep nesting doesn't overflow the stack
|
||||
Hex = ""
|
||||
For i = 1 To 10000
|
||||
Hex = Hex + "91"
|
||||
Next
|
||||
*MsgPack = WrapHex(Hex + "01")
|
||||
AssertEqualsQ(MsgPackGetFieldFullSize(*MsgPack), 10001, "Deeply nested arrays")
|
||||
FreeWrappedHex(*MsgPack)
|
||||
|
||||
|
||||
; Entry counts
|
||||
*MsgPack = WrapHex("DC0003010203")
|
||||
AssertEqualsQ(MsgPackGetEntryCount(*MsgPack), 3, "Array16 entry count")
|
||||
FreeWrappedHex(*MsgPack)
|
||||
|
||||
*MsgPack = WrapHex("82A16101A16202")
|
||||
AssertEqualsQ(MsgPackGetEntryCount(*MsgPack), 2, "Map entry count")
|
||||
FreeWrappedHex(*MsgPack)
|
||||
|
||||
*MsgPack = WrapHex("01")
|
||||
AssertEqualsQ(MsgPackGetEntryCount(*MsgPack), #MsgPack_InvalidSize, "Fixint has no entry count")
|
||||
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_InvalidFormatCode, "Entry count error is set")
|
||||
FreeWrappedHex(*MsgPack)
|
||||
|
||||
|
||||
; Skipping
|
||||
*MsgPack = WrapHex("01A161C0")
|
||||
AssertEquals(MsgPackSkip(*MsgPack, 2), #MsgPack_Error_Success, "Skipped 2 fields")
|
||||
AssertEquals(*MsgPack\BufferOffset, 3, "Offset after skipping 2 fields")
|
||||
AssertEquals(MsgPackSkip(*MsgPack, 2), #MsgPack_Error_BufferTooSmall, "Cannot skip past the end")
|
||||
AssertEquals(*MsgPack\BufferOffset, 3, "Offset is untouched by the failed skip")
|
||||
AssertEquals(MsgPackSkip(*MsgPack), #MsgPack_Error_Success, "Skipped the last field")
|
||||
AssertIsTrue(Bool(_MsgPackIsAtTheEnd(*MsgPack)), "Reached the end by skipping")
|
||||
AssertEquals(MsgPackSkip(*MsgPack), #MsgPack_Error_AtOrPastTheEndOfBuffer, "Cannot skip at the end")
|
||||
FreeWrappedHex(*MsgPack)
|
||||
|
||||
|
||||
; Iterating on an array: [1, "ab", [true, nil], {"k": [1, 2]}, 3] followed by `false`
|
||||
*MsgPack = WrapHex("9501A26162" + "92C3C0" + "81A16B920102" + "03" + "C2")
|
||||
|
||||
*Iterator = MsgPackExamine(*MsgPack)
|
||||
AssertIsTrue(Bool(*Iterator <> #Null), "Examined the array")
|
||||
AssertEquals(*MsgPack\BufferOffset, 15, "Offset was moved past the array")
|
||||
AssertEquals(*Iterator\BufferSize, 14, "View is limited to the array's content")
|
||||
|
||||
EntryCount = 0
|
||||
While MsgPackNextEntry(*Iterator)
|
||||
EntryCount = EntryCount + 1
|
||||
Select EntryCount
|
||||
Case 1
|
||||
AssertEqualsQ(MsgPackReadInteger(*Iterator), 1, "Entry #1")
|
||||
Case 2
|
||||
AssertIsTrue(Bool(MsgPackReadString(*Iterator) = "ab"), "Entry #2")
|
||||
Case 3
|
||||
; Left unread, must be skipped as a whole
|
||||
AssertEquals(MsgPackFormatCodeToDataType(MsgPackGetFormatCode(*Iterator)), #MsgPack_DataTypes_Array, "Entry #3 is an array")
|
||||
Case 4
|
||||
*Nested = MsgPackExamine(*Iterator)
|
||||
AssertIsTrue(Bool(*Nested <> #Null), "Examined the nested map")
|
||||
AssertIsTrue(MsgPackNextEntry(*Nested), "Nested map has an entry")
|
||||
AssertIsTrue(Bool(MsgPackReadString(*Nested) = "k"), "Nested map key")
|
||||
AssertEqualsQ(MsgPackGetEntryCount(*Nested), 2, "Nested map value is a 2 entries array")
|
||||
AssertIsFalse(MsgPackNextEntry(*Nested), "Nested map has a single entry")
|
||||
AssertEquals(MsgPackLastError(*Nested), #MsgPack_Error_Success, "Nested map ended without errors")
|
||||
MsgPackFinish(*Nested)
|
||||
Case 5
|
||||
AssertEqualsQ(MsgPackReadInteger(*Iterator), 3, "Entry #5")
|
||||
EndSelect
|
||||
Wend
|
||||
|
||||
AssertEquals(EntryCount, 5, "Iterated on 5 entries")
|
||||
AssertEquals(MsgPackLastError(*Iterator), #MsgPack_Error_Success, "Array ended without errors")
|
||||
AssertIsFalse(MsgPackNextEntry(*Iterator), "Iteration stays over")
|
||||
MsgPackFinish(*Iterator)
|
||||
|
||||
AssertIsFalse(MsgPackReadBoolean(*MsgPack), "Read the field following the array")
|
||||
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_Success, "Field following the array was read properly")
|
||||
FreeWrappedHex(*MsgPack)
|
||||
|
||||
|
||||
; Iterating on a map while only reading the keys: {"a": 1, "b": [2, 3]}
|
||||
*MsgPack = WrapHex("82A16101A16292020305")
|
||||
|
||||
*Iterator = MsgPackExamine(*MsgPack)
|
||||
AssertIsTrue(MsgPackNextEntry(*Iterator), "Map entry #1")
|
||||
AssertIsTrue(Bool(MsgPackReadString(*Iterator) = "a"), "Map key #1")
|
||||
AssertIsTrue(MsgPackNextEntry(*Iterator), "Map entry #2")
|
||||
AssertIsTrue(Bool(MsgPackReadString(*Iterator) = "b"), "Map key #2")
|
||||
AssertEquals(MsgPackSkip(*Iterator), #MsgPack_Error_Success, "Skipped map value #2")
|
||||
AssertEqualsQ(MsgPackReadInteger(*Iterator), 0, "Cannot read past the map's end")
|
||||
AssertEquals(MsgPackLastError(*Iterator), #MsgPack_Error_AtOrPastTheEndOfBuffer, "Map's end error is set")
|
||||
AssertIsFalse(MsgPackNextEntry(*Iterator), "Map has 2 entries")
|
||||
MsgPackFinish(*Iterator)
|
||||
|
||||
AssertEqualsQ(MsgPackReadInteger(*MsgPack), 5, "Read the field following the map")
|
||||
FreeWrappedHex(*MsgPack)
|
||||
|
||||
|
||||
; Empty arrays and maps
|
||||
*MsgPack = WrapHex("9080")
|
||||
|
||||
*Iterator = MsgPackExamine(*MsgPack)
|
||||
AssertIsTrue(Bool(*Iterator <> #Null), "Examined the empty array")
|
||||
AssertIsFalse(MsgPackNextEntry(*Iterator), "Empty array has no entries")
|
||||
MsgPackFinish(*Iterator)
|
||||
|
||||
*Iterator = MsgPackExamine(*MsgPack)
|
||||
AssertIsTrue(Bool(*Iterator <> #Null), "Examined the empty map")
|
||||
AssertIsFalse(MsgPackNextEntry(*Iterator), "Empty map has no entries")
|
||||
MsgPackFinish(*Iterator)
|
||||
|
||||
FreeWrappedHex(*MsgPack)
|
||||
|
||||
|
||||
; Examination errors
|
||||
*MsgPack = WrapHex("01")
|
||||
AssertIsTrue(Bool(MsgPackExamine(*MsgPack) = #Null), "Cannot examine a fixint")
|
||||
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_InvalidFormatCode, "Examination error is set")
|
||||
FreeWrappedHex(*MsgPack)
|
||||
|
||||
*MsgPack = WrapHex("920191")
|
||||
AssertIsTrue(Bool(MsgPackExamine(*MsgPack) = #Null), "Cannot examine a truncated array")
|
||||
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_BufferTooSmall, "Truncated array error is set")
|
||||
AssertEquals(*MsgPack\BufferOffset, 0, "Offset is untouched by the failed examination")
|
||||
FreeWrappedHex(*MsgPack)
|
||||
|
||||
|
||||
; Unsafe examination: {"a": [1], "b": 2} followed by `5`
|
||||
*MsgPack = WrapHex("82A1619101A1620205")
|
||||
|
||||
*Iterator = MsgPackExamine(*MsgPack, #MsgPack_ExaminationOptions_Unsafe)
|
||||
AssertIsTrue(Bool(*Iterator <> #Null), "Examined the map unsafely")
|
||||
AssertEquals(*MsgPack\BufferOffset, 0, "Unsafe examination doesn't move the offset")
|
||||
AssertEquals(*Iterator\BufferSize, 8, "Unsafe view goes up to the end of the buffer")
|
||||
|
||||
EntryCount = 0
|
||||
While MsgPackNextEntry(*Iterator)
|
||||
EntryCount = EntryCount + 1
|
||||
If EntryCount = 2
|
||||
AssertIsTrue(Bool(MsgPackReadString(*Iterator) = "b"), "Unsafe map key #2")
|
||||
AssertEqualsQ(MsgPackReadInteger(*Iterator), 2, "Unsafe map value #2")
|
||||
EndIf
|
||||
Wend
|
||||
|
||||
AssertEquals(EntryCount, 2, "Unsafe map has 2 entries")
|
||||
AssertEquals(MsgPackLastError(*Iterator), #MsgPack_Error_Success, "Unsafe map ended without errors")
|
||||
AssertEquals(*Iterator\BufferSize, 7, "Unsafe view is limited once its end is reached")
|
||||
AssertEqualsQ(MsgPackReadInteger(*Iterator), 0, "Cannot read past the unsafe map's end")
|
||||
AssertEquals(MsgPackLastError(*Iterator), #MsgPack_Error_AtOrPastTheEndOfBuffer, "Unsafe map's end error is set")
|
||||
MsgPackFinish(*Iterator)
|
||||
|
||||
AssertEquals(MsgPackSkip(*MsgPack), #MsgPack_Error_Success, "Skipped the unsafely examined map")
|
||||
AssertEqualsQ(MsgPackReadInteger(*MsgPack), 5, "Read the field following the unsafe map")
|
||||
FreeWrappedHex(*MsgPack)
|
||||
|
||||
; Invalid content is only detected while iterating
|
||||
*MsgPack = WrapHex("9201C1")
|
||||
|
||||
*Iterator = MsgPackExamine(*MsgPack, #MsgPack_ExaminationOptions_Unsafe)
|
||||
AssertIsTrue(Bool(*Iterator <> #Null), "Examined the invalid array unsafely")
|
||||
AssertIsTrue(MsgPackNextEntry(*Iterator), "Invalid array entry #1")
|
||||
AssertIsTrue(MsgPackNextEntry(*Iterator), "Invalid array entry #2 is reached")
|
||||
AssertIsFalse(MsgPackNextEntry(*Iterator), "Cannot skip the invalid entry #2")
|
||||
AssertEquals(MsgPackLastError(*Iterator), #MsgPack_Error_InvalidFormatCode, "Invalid entry error is set")
|
||||
MsgPackFinish(*Iterator)
|
||||
FreeWrappedHex(*MsgPack)
|
||||
|
||||
|
||||
|
||||
If FailedUnitTests
|
||||
End 1
|
||||
EndIf
|
||||
|
||||
Debug "Done !"
|
||||
|
||||
End 0
|
||||
Reference in New Issue
Block a user