Updated primitives to new helper-based structure, Added generic reader/writer too

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-10-28 00:19:27 +01:00
parent 7e6b3dab21
commit 56464afd3b
5 changed files with 549 additions and 208 deletions
+26
View File
@@ -131,6 +131,32 @@ PureBasic include for creating/reading MessagePack data.
Returns `#MsgPack_Error_Success` on success, something else otherwise.
### Integers & Floats
* `MsgPackWriteInteger.MsgPack_ErrorCode(*MsgPackData.MsgPackData, Value.q)` \
Writes `Value` using the smallest int format code that fits, fixints included. \
Positive values always use the unsigned format codes. \
UInt64 values above `(2^63)-1` must be written with `MsgPackWriteUInt64`.
* `MsgPackWriteFixInt.MsgPack_ErrorCode(*MsgPackData.MsgPackData, Value.q)` \
Writes `Value` as a fixint, from `-32` to `127`. \
Returns `#MsgPack_Error_ValueOverflow` if it doesn't fit.
* `MsgPackWrite/Read[U]Int8/16/32/64`, `MsgPackWrite/ReadFloat32/64` \
Force the given format code. Aliases: `MsgPackWrite/ReadU8`, `I8`, ..., `F64`. \
`UInt32` and `UInt64` use `.l` and `.q`, so their values above the signed max come back negative.
* `MsgPackReadInteger.q(*MsgPackData.MsgPackData)` \
Reads any int field at the current internal offset, fixints included. \
Returns `0` on errors and sets the internal error code. \
UInt64 values above `(2^63)-1` fail with `#MsgPack_Error_ValueOverflow` without moving the offset, so they can be read with `MsgPackReadUInt64`. \
See: `MsgPackLastError`
* `MsgPackReadFloat.d(*MsgPackData.MsgPackData)` \
Reads any float field at the current internal offset. \
Returns `0` on errors and sets the internal error code. \
See: `MsgPackLastError`
### Binary
* `MsgPackWriteBinary.MsgPack_ErrorCode(*MsgPackData.MsgPackData, *Buffer, BufferSize.q)` \
Writes `BufferSize` bytes from `*Buffer` using the smallest bin format code that fits. \
+237 -208
View File
@@ -26,6 +26,7 @@ CompilerEndIf
; Includes the required "Endianness.pbi"
XIncludeFile "./_Commons.pbi"
XIncludeFile "./_Allocators.pbi"
XIncludeFile "./_Utils.pbi"
@@ -96,255 +97,283 @@ EndProcedure
;-> Getters & Setters
; The "fixed field" helpers below handle every `[FormatCode][Value]` layout
; shared by the int and float families.
; The `ValueByteLength` parameter is the number of bytes used to store the value:
; * `0` - Value is stored in the format code's lower bits. (FixIntPositive, FixIntNegative)
; * `1` - UInt8, Int8
; * `2` - UInt16, Int16
; * `4` - UInt32, Int32, Float32
; * `8` - UInt64, Int64, Float64
; Values are passed around as raw bits in a quad, floats included.
;-> > UInt8
Procedure.MsgPack_ErrorCode MsgPackWriteUInt8(*MsgPackData.MsgPackData, Value.a)
; Reserves space for the whole field, writes its format code and big-endian value,
; and moves the offset right after them.
; Sets the structure's error code and returns it.
Procedure.MsgPack_ErrorCode _MsgPackWriteFixedField(*MsgPackData.MsgPackData, FormatCode.MsgPack_FormatCode, ValueByteLength.a, Value.q)
Protected ErrorCode.MsgPack_ErrorCode
ErrorCode = _MsgPackReserveWrite(*MsgPackData, 2)
ErrorCode = _MsgPackReserveWrite(*MsgPackData, 1 + ValueByteLength)
If ErrorCode <> #MsgPack_Error_Success
ProcedureReturn ErrorCode
EndIf
PokeA(*MsgPackData\Buffer + *MsgPackData\BufferOffset, #MsgPack_FormatCode_UInt8)
PokeA(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1, Value)
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + 2
Select ValueByteLength
Case 0
PokeA(*MsgPackData\Buffer + *MsgPackData\BufferOffset, FormatCode | (Value & $FF))
Case 1
PokeA(*MsgPackData\Buffer + *MsgPackData\BufferOffset, FormatCode)
PokeA(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1, Value)
Case 2
PokeA(*MsgPackData\Buffer + *MsgPackData\BufferOffset, FormatCode)
PokeU(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1, EndianSwapU16(Value))
Case 4
PokeA(*MsgPackData\Buffer + *MsgPackData\BufferOffset, FormatCode)
PokeL(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1, EndianSwapI32(Value))
Case 8
PokeA(*MsgPackData\Buffer + *MsgPackData\BufferOffset, FormatCode)
PokeQ(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1, EndianSwapI64(Value))
EndSelect
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + 1 + ValueByteLength
ProcedureReturn #MsgPack_Error_Success
EndProcedure
Procedure.a MsgPackReadUInt8(*MsgPackData.MsgPackData)
Protected ReturnedValue.a
If _MsgPackExpectRead(*MsgPackData, #MsgPack_FormatCode_UInt8, 2) <> #MsgPack_Error_Success
; Reads the value of the field at the current offset if its format code is `FormatCode`.
; Doesn't support fixints, since their format code varies with the value.
; Returns: The value's raw bits, zero-extended. (e.g.: `$FF` for an Int8 `-1`)
; OnError: Returns `0` and sets the `MsgPackData\LastError` field.
Procedure.q _MsgPackReadFixedField(*MsgPackData.MsgPackData, FormatCode.MsgPack_FormatCode, ValueByteLength.a)
Protected ReturnedValue.q
If _MsgPackExpectRead(*MsgPackData, FormatCode, 1 + ValueByteLength) <> #MsgPack_Error_Success
ProcedureReturn 0
EndIf
ReturnedValue = PeekA(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1)
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + 2
Select ValueByteLength
Case 1
ReturnedValue = PeekA(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1)
Case 2
ReturnedValue = EndianSwapU16(PeekU(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1))
Case 4
ReturnedValue = EndianSwapI32(PeekL(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1)) & $FFFFFFFF
Case 8
ReturnedValue = EndianSwapI64(PeekQ(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1))
EndSelect
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + 1 + ValueByteLength
ProcedureReturn ReturnedValue
EndProcedure
;-> Getters & Setters
;-> > UInt8
Procedure.MsgPack_ErrorCode MsgPackWriteUInt8(*MsgPackData.MsgPackData, Value.a)
ProcedureReturn _MsgPackWriteFixedField(*MsgPackData, #MsgPack_FormatCode_UInt8, 1, Value)
EndProcedure
Procedure.a MsgPackReadUInt8(*MsgPackData.MsgPackData)
ProcedureReturn _MsgPackReadFixedField(*MsgPackData, #MsgPack_FormatCode_UInt8, 1)
EndProcedure
;-> > Int8
Procedure.MsgPack_ErrorCode MsgPackWriteInt8(*MsgPackData.MsgPackData, Value.b)
Protected ErrorCode.MsgPack_ErrorCode
ErrorCode = _MsgPackReserveWrite(*MsgPackData, 2)
If ErrorCode <> #MsgPack_Error_Success
ProcedureReturn ErrorCode
EndIf
PokeA(*MsgPackData\Buffer + *MsgPackData\BufferOffset, #MsgPack_FormatCode_Int8)
PokeB(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1, Value)
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + 2
ProcedureReturn #MsgPack_Error_Success
ProcedureReturn _MsgPackWriteFixedField(*MsgPackData, #MsgPack_FormatCode_Int8, 1, Value)
EndProcedure
Procedure.b MsgPackReadInt8(*MsgPackData.MsgPackData)
Protected ReturnedValue.b
If _MsgPackExpectRead(*MsgPackData, #MsgPack_FormatCode_Int8, 2) <> #MsgPack_Error_Success
ProcedureReturn 0
EndIf
ReturnedValue = PeekB(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1)
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + 2
ProcedureReturn ReturnedValue
ProcedureReturn _MsgPackReadFixedField(*MsgPackData, #MsgPack_FormatCode_Int8, 1)
EndProcedure
;-> > UInt16
Procedure.MsgPack_ErrorCode MsgPackWriteUInt16(*MsgPackData.MsgPackData, Value.u)
Protected ErrorCode.MsgPack_ErrorCode
ErrorCode = _MsgPackReserveWrite(*MsgPackData, 3)
If ErrorCode <> #MsgPack_Error_Success
ProcedureReturn ErrorCode
EndIf
PokeA(*MsgPackData\Buffer + *MsgPackData\BufferOffset, #MsgPack_FormatCode_UInt16)
PokeU(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1, EndianSwapU16(Value))
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + 3
ProcedureReturn #MsgPack_Error_Success
ProcedureReturn _MsgPackWriteFixedField(*MsgPackData, #MsgPack_FormatCode_UInt16, 2, Value)
EndProcedure
Procedure.u MsgPackReadUInt16(*MsgPackData.MsgPackData)
Protected ReturnedValue.u
If _MsgPackExpectRead(*MsgPackData, #MsgPack_FormatCode_UInt16, 3) <> #MsgPack_Error_Success
ProcedureReturn 0
EndIf
ReturnedValue = EndianSwapU16(PeekU(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1))
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + 3
ProcedureReturn ReturnedValue
ProcedureReturn _MsgPackReadFixedField(*MsgPackData, #MsgPack_FormatCode_UInt16, 2)
EndProcedure
;-> > > Int16
Procedure.MsgPack_ErrorCode MsgPackWriteInt16(*MsgPackData.MsgPackData, Value.w)
Protected ErrorCode.MsgPack_ErrorCode
ErrorCode = _MsgPackReserveWrite(*MsgPackData, 3)
If ErrorCode <> #MsgPack_Error_Success
ProcedureReturn ErrorCode
EndIf
PokeA(*MsgPackData\Buffer + *MsgPackData\BufferOffset, #MsgPack_FormatCode_Int16)
PokeW(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1, EndianSwapI16(Value))
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + 3
ProcedureReturn #MsgPack_Error_Success
ProcedureReturn _MsgPackWriteFixedField(*MsgPackData, #MsgPack_FormatCode_Int16, 2, Value)
EndProcedure
Procedure.w MsgPackReadInt16(*MsgPackData.MsgPackData)
Protected ReturnedValue.w
If _MsgPackExpectRead(*MsgPackData, #MsgPack_FormatCode_Int16, 3) <> #MsgPack_Error_Success
ProcedureReturn 0
EndIf
ReturnedValue = EndianSwapI16(PeekW(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1))
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + 3
ProcedureReturn ReturnedValue
ProcedureReturn _MsgPackReadFixedField(*MsgPackData, #MsgPack_FormatCode_Int16, 2)
EndProcedure
;-> > > UInt32
; No unsigned Long in PureBasic: value is peeked/poked as-is via .l and returned unchanged.
Procedure.MsgPack_ErrorCode MsgPackWriteUInt32(*MsgPackData.MsgPackData, Value.l)
Protected ErrorCode.MsgPack_ErrorCode
ErrorCode = _MsgPackReserveWrite(*MsgPackData, 5)
If ErrorCode <> #MsgPack_Error_Success
ProcedureReturn ErrorCode
EndIf
PokeA(*MsgPackData\Buffer + *MsgPackData\BufferOffset, #MsgPack_FormatCode_UInt32)
PokeL(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1, EndianSwapI32(Value))
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + 5
ProcedureReturn #MsgPack_Error_Success
ProcedureReturn _MsgPackWriteFixedField(*MsgPackData, #MsgPack_FormatCode_UInt32, 4, Value)
EndProcedure
Procedure.l MsgPackReadUInt32(*MsgPackData.MsgPackData)
Protected ReturnedValue.l
If _MsgPackExpectRead(*MsgPackData, #MsgPack_FormatCode_UInt32, 5) <> #MsgPack_Error_Success
ProcedureReturn 0
EndIf
ReturnedValue = EndianSwapI32(PeekL(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1))
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + 5
ProcedureReturn ReturnedValue
ProcedureReturn _MsgPackReadFixedField(*MsgPackData, #MsgPack_FormatCode_UInt32, 4)
EndProcedure
;-> > > Int32
Procedure.MsgPack_ErrorCode MsgPackWriteInt32(*MsgPackData.MsgPackData, Value.l)
Protected ErrorCode.MsgPack_ErrorCode
ErrorCode = _MsgPackReserveWrite(*MsgPackData, 5)
If ErrorCode <> #MsgPack_Error_Success
ProcedureReturn ErrorCode
EndIf
PokeA(*MsgPackData\Buffer + *MsgPackData\BufferOffset, #MsgPack_FormatCode_Int32)
PokeL(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1, EndianSwapI32(Value))
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + 5
ProcedureReturn #MsgPack_Error_Success
ProcedureReturn _MsgPackWriteFixedField(*MsgPackData, #MsgPack_FormatCode_Int32, 4, Value)
EndProcedure
Procedure.l MsgPackReadInt32(*MsgPackData.MsgPackData)
Protected ReturnedValue.l
If _MsgPackExpectRead(*MsgPackData, #MsgPack_FormatCode_Int32, 5) <> #MsgPack_Error_Success
ProcedureReturn 0
EndIf
ReturnedValue = EndianSwapI32(PeekL(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1))
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + 5
ProcedureReturn ReturnedValue
ProcedureReturn _MsgPackReadFixedField(*MsgPackData, #MsgPack_FormatCode_Int32, 4)
EndProcedure
;-> > > UInt64
; No unsigned Quad in PureBasic: value is peeked/poked as-is via .q and returned unchanged.
Procedure.MsgPack_ErrorCode MsgPackWriteUInt64(*MsgPackData.MsgPackData, Value.q)
Protected ErrorCode.MsgPack_ErrorCode
ErrorCode = _MsgPackReserveWrite(*MsgPackData, 9)
If ErrorCode <> #MsgPack_Error_Success
ProcedureReturn ErrorCode
EndIf
PokeA(*MsgPackData\Buffer + *MsgPackData\BufferOffset, #MsgPack_FormatCode_UInt64)
PokeQ(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1, EndianSwapI64(Value))
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + 9
ProcedureReturn #MsgPack_Error_Success
ProcedureReturn _MsgPackWriteFixedField(*MsgPackData, #MsgPack_FormatCode_UInt64, 8, Value)
EndProcedure
Procedure.q MsgPackReadUInt64(*MsgPackData.MsgPackData)
Protected ReturnedValue.q
If _MsgPackExpectRead(*MsgPackData, #MsgPack_FormatCode_UInt64, 9) <> #MsgPack_Error_Success
ProcedureReturn 0
EndIf
ReturnedValue = EndianSwapI64(PeekQ(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1))
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + 9
ProcedureReturn ReturnedValue
ProcedureReturn _MsgPackReadFixedField(*MsgPackData, #MsgPack_FormatCode_UInt64, 8)
EndProcedure
;-> > > Int64
Procedure.MsgPack_ErrorCode MsgPackWriteInt64(*MsgPackData.MsgPackData, Value.q)
Protected ErrorCode.MsgPack_ErrorCode
ErrorCode = _MsgPackReserveWrite(*MsgPackData, 9)
If ErrorCode <> #MsgPack_Error_Success
ProcedureReturn ErrorCode
EndIf
PokeA(*MsgPackData\Buffer + *MsgPackData\BufferOffset, #MsgPack_FormatCode_Int64)
PokeQ(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1, EndianSwapI64(Value))
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + 9
ProcedureReturn #MsgPack_Error_Success
ProcedureReturn _MsgPackWriteFixedField(*MsgPackData, #MsgPack_FormatCode_Int64, 8, Value)
EndProcedure
Procedure.q MsgPackReadInt64(*MsgPackData.MsgPackData)
ProcedureReturn _MsgPackReadFixedField(*MsgPackData, #MsgPack_FormatCode_Int64, 8)
EndProcedure
;-> > > FixInt
; Writes `Value` as a positive (`0` to `127`) or negative (`-32` to `-1`) fixint.
; Returns `#MsgPack_Error_Success` on success, something else otherwise.
Procedure.MsgPack_ErrorCode MsgPackWriteFixInt(*MsgPackData.MsgPackData, Value.q)
CompilerIf Not #MsgPack_DisableNullChecks
If Not *MsgPackData
DebuggerError("A #Null MsgPackData pointer was passed !")
ProcedureReturn #MsgPack_Error_NullPointerGiven
EndIf
CompilerEndIf
If Value >= 0 And Value <= 127
ProcedureReturn _MsgPackWriteFixedField(*MsgPackData, #MsgPack_FormatCode_FixIntPositive, 0, Value)
ElseIf Value >= -32 And Value < 0
ProcedureReturn _MsgPackWriteFixedField(*MsgPackData, #MsgPack_FormatCode_FixIntNegative, 0, Value)
EndIf
DebuggerWarning("Value doesn't fit in a fixint: " + Str(Value))
*MsgPackData\LastError = #MsgPack_Error_ValueOverflow
ProcedureReturn #MsgPack_Error_ValueOverflow
EndProcedure
;-> > > Any integer
; Writes `Value` using the smallest int format code that fits.
; Positive values always use the unsigned format codes, as recommended by the spec.
; Returns `#MsgPack_Error_Success` on success, something else otherwise.
; Remark: UInt64 values above `(2^63)-1` must be written with `MsgPackWriteUInt64()`.
Procedure.MsgPack_ErrorCode MsgPackWriteInteger(*MsgPackData.MsgPackData, Value.q)
If Value >= 0
If Value <= 127
ProcedureReturn _MsgPackWriteFixedField(*MsgPackData, #MsgPack_FormatCode_FixIntPositive, 0, Value)
ElseIf Value <= 255
ProcedureReturn _MsgPackWriteFixedField(*MsgPackData, #MsgPack_FormatCode_UInt8, 1, Value)
ElseIf Value <= 65535
ProcedureReturn _MsgPackWriteFixedField(*MsgPackData, #MsgPack_FormatCode_UInt16, 2, Value)
ElseIf Value <= 4294967295
ProcedureReturn _MsgPackWriteFixedField(*MsgPackData, #MsgPack_FormatCode_UInt32, 4, Value)
Else
ProcedureReturn _MsgPackWriteFixedField(*MsgPackData, #MsgPack_FormatCode_UInt64, 8, Value)
EndIf
Else
If Value >= -32
ProcedureReturn _MsgPackWriteFixedField(*MsgPackData, #MsgPack_FormatCode_FixIntNegative, 0, Value)
ElseIf Value >= -128
ProcedureReturn _MsgPackWriteFixedField(*MsgPackData, #MsgPack_FormatCode_Int8, 1, Value)
ElseIf Value >= -32768
ProcedureReturn _MsgPackWriteFixedField(*MsgPackData, #MsgPack_FormatCode_Int16, 2, Value)
ElseIf Value >= -2147483648
ProcedureReturn _MsgPackWriteFixedField(*MsgPackData, #MsgPack_FormatCode_Int32, 4, Value)
Else
ProcedureReturn _MsgPackWriteFixedField(*MsgPackData, #MsgPack_FormatCode_Int64, 8, Value)
EndIf
EndIf
EndProcedure
; Reads any int field at the current offset, fixints included.
; Returns: The value as a signed quad.
; OnError: Returns `0` and sets the `MsgPackData\LastError` field.
; UInt64 values above `(2^63)-1` fail with `#MsgPack_Error_ValueOverflow` and
; leave the offset untouched, so they can be read with `MsgPackReadUInt64()`.
Procedure.q MsgPackReadInteger(*MsgPackData.MsgPackData)
Protected ReturnedValue.q
If _MsgPackExpectRead(*MsgPackData, #MsgPack_FormatCode_Int64, 9) <> #MsgPack_Error_Success
CompilerIf Not #MsgPack_DisableNullChecks
If Not *MsgPackData
DebuggerError("A #Null MsgPackData pointer was passed !")
ProcedureReturn 0
EndIf
CompilerEndIf
If _MsgPackIsAtTheEnd(*MsgPackData)
Debug("End of buffer reached, cannot check data format code !")
*MsgPackData\LastError = #MsgPack_Error_AtOrPastTheEndOfBuffer
ProcedureReturn 0
EndIf
ReturnedValue = EndianSwapI64(PeekQ(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1))
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + 9
ProcedureReturn ReturnedValue
Select MsgPackGetFormatCode(*MsgPackData)
Case #MsgPack_FormatCode_FixIntPositive,
#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
*MsgPackData\LastError = #MsgPack_Error_Success
ProcedureReturn ReturnedValue
Case #MsgPack_FormatCode_UInt8:
ProcedureReturn _MsgPackReadFixedField(*MsgPackData, #MsgPack_FormatCode_UInt8, 1)
Case #MsgPack_FormatCode_UInt16:
ProcedureReturn _MsgPackReadFixedField(*MsgPackData, #MsgPack_FormatCode_UInt16, 2)
Case #MsgPack_FormatCode_UInt32:
ProcedureReturn _MsgPackReadFixedField(*MsgPackData, #MsgPack_FormatCode_UInt32, 4)
Case #MsgPack_FormatCode_UInt64:
If _MsgPackHasSpaceLeft(*MsgPackData, 9) And (PeekA(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1) & $80)
Debug("UInt64 value doesn't fit in a signed quad !")
*MsgPackData\LastError = #MsgPack_Error_ValueOverflow
ProcedureReturn 0
EndIf
ProcedureReturn _MsgPackReadFixedField(*MsgPackData, #MsgPack_FormatCode_UInt64, 8)
Case #MsgPack_FormatCode_Int8:
ProcedureReturn MsgPackReadInt8(*MsgPackData)
Case #MsgPack_FormatCode_Int16:
ProcedureReturn MsgPackReadInt16(*MsgPackData)
Case #MsgPack_FormatCode_Int32:
ProcedureReturn MsgPackReadInt32(*MsgPackData)
Case #MsgPack_FormatCode_Int64:
ProcedureReturn MsgPackReadInt64(*MsgPackData)
EndSelect
Debug("Unexpected format code ! (" + RSet(Hex(PeekA(*MsgPackData\Buffer + *MsgPackData\BufferOffset)), 2, "0") + ")")
*MsgPackData\LastError = #MsgPack_Error_InvalidFormatCode
ProcedureReturn 0
EndProcedure
@@ -353,31 +382,15 @@ EndProcedure
; The value's bits are moved and swapped as an integer, since loading a byte-swapped
; float into the FPU can alter its bits. (e.g.: NaN quieting on x87)
Procedure.MsgPack_ErrorCode MsgPackWriteFloat32(*MsgPackData.MsgPackData, Value.f)
Protected ErrorCode.MsgPack_ErrorCode
ErrorCode = _MsgPackReserveWrite(*MsgPackData, 5)
If ErrorCode <> #MsgPack_Error_Success
ProcedureReturn ErrorCode
EndIf
PokeA(*MsgPackData\Buffer + *MsgPackData\BufferOffset, #MsgPack_FormatCode_Float32)
PokeL(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1, EndianSwapI32(PeekL(@Value)))
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + 5
ProcedureReturn #MsgPack_Error_Success
ProcedureReturn _MsgPackWriteFixedField(*MsgPackData, #MsgPack_FormatCode_Float32, 4, PeekL(@Value))
EndProcedure
Procedure.f MsgPackReadFloat32(*MsgPackData.MsgPackData)
Protected ReturnedValue.f
If _MsgPackExpectRead(*MsgPackData, #MsgPack_FormatCode_Float32, 5) <> #MsgPack_Error_Success
ProcedureReturn 0
EndIf
PokeL(@ReturnedValue, EndianSwapI32(PeekL(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1)))
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + 5
PokeL(@ReturnedValue, _MsgPackReadFixedField(*MsgPackData, #MsgPack_FormatCode_Float32, 4))
ProcedureReturn ReturnedValue
EndProcedure
@@ -387,32 +400,48 @@ EndProcedure
; The value's bits are moved and swapped as an integer, since loading a byte-swapped
; float into the FPU can alter its bits. (e.g.: NaN quieting on x87)
Procedure.MsgPack_ErrorCode MsgPackWriteFloat64(*MsgPackData.MsgPackData, Value.d)
Protected ErrorCode.MsgPack_ErrorCode
ErrorCode = _MsgPackReserveWrite(*MsgPackData, 9)
If ErrorCode <> #MsgPack_Error_Success
ProcedureReturn ErrorCode
EndIf
PokeA(*MsgPackData\Buffer + *MsgPackData\BufferOffset, #MsgPack_FormatCode_Float64)
PokeQ(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1, EndianSwapI64(PeekQ(@Value)))
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + 9
ProcedureReturn #MsgPack_Error_Success
ProcedureReturn _MsgPackWriteFixedField(*MsgPackData, #MsgPack_FormatCode_Float64, 8, PeekQ(@Value))
EndProcedure
Procedure.d MsgPackReadFloat64(*MsgPackData.MsgPackData)
Protected ReturnedValue.d
If _MsgPackExpectRead(*MsgPackData, #MsgPack_FormatCode_Float64, 9) <> #MsgPack_Error_Success
PokeQ(@ReturnedValue, _MsgPackReadFixedField(*MsgPackData, #MsgPack_FormatCode_Float64, 8))
ProcedureReturn ReturnedValue
EndProcedure
;-> > > Any float
; Reads any float field at the current offset.
; Returns: The value as a double, float32 values being widened losslessly.
; OnError: Returns `0` and sets the `MsgPackData\LastError` field.
Procedure.d MsgPackReadFloat(*MsgPackData.MsgPackData)
CompilerIf Not #MsgPack_DisableNullChecks
If Not *MsgPackData
DebuggerError("A #Null MsgPackData pointer was passed !")
ProcedureReturn 0
EndIf
CompilerEndIf
If _MsgPackIsAtTheEnd(*MsgPackData)
Debug("End of buffer reached, cannot check data format code !")
*MsgPackData\LastError = #MsgPack_Error_AtOrPastTheEndOfBuffer
ProcedureReturn 0
EndIf
PokeQ(@ReturnedValue, EndianSwapI64(PeekQ(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1)))
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + 9
ProcedureReturn ReturnedValue
Select MsgPackGetFormatCode(*MsgPackData)
Case #MsgPack_FormatCode_Float32:
ProcedureReturn MsgPackReadFloat32(*MsgPackData)
Case #MsgPack_FormatCode_Float64:
ProcedureReturn MsgPackReadFloat64(*MsgPackData)
EndSelect
Debug("Unexpected format code ! (" + RSet(Hex(PeekA(*MsgPackData\Buffer + *MsgPackData\BufferOffset)), 2, "0") + ")")
*MsgPackData\LastError = #MsgPack_Error_InvalidFormatCode
ProcedureReturn 0
EndProcedure
@@ -0,0 +1,34 @@
EnableExplicit
XIncludeFile "../_UnitTesting.pbi"
XIncludeFile "../../Includes/MessagePack.pbi"
; /!\ Every case here calls `DebuggerError()`, which halts the program under the debugger.
; Run it without the debugger and check the exit code.
; Null MsgPack pointers
AssertEquals(MsgPackWriteInteger(#Null, 1), #MsgPack_Error_NullPointerGiven, "MsgPackWriteInteger is given #Null for a fixint")
AssertEquals(MsgPackWriteInteger(#Null, 100000), #MsgPack_Error_NullPointerGiven, "MsgPackWriteInteger is given #Null for a UInt32")
AssertEquals(MsgPackWriteInteger(#Null, -100), #MsgPack_Error_NullPointerGiven, "MsgPackWriteInteger is given #Null for an Int8")
AssertEquals(MsgPackWriteFixInt(#Null, 1), #MsgPack_Error_NullPointerGiven, "MsgPackWriteFixInt is given #Null")
AssertEquals(MsgPackWriteFixInt(#Null, 1000), #MsgPack_Error_NullPointerGiven, "MsgPackWriteFixInt is given #Null before the range check")
AssertEquals(MsgPackWriteUInt8(#Null, 1), #MsgPack_Error_NullPointerGiven, "MsgPackWriteUInt8 is given #Null")
AssertEquals(MsgPackWriteInt64(#Null, 1), #MsgPack_Error_NullPointerGiven, "MsgPackWriteInt64 is given #Null")
AssertEqualsQ(MsgPackReadInteger(#Null), 0, "MsgPackReadInteger is given #Null")
AssertEqualsA(MsgPackReadUInt8(#Null), 0, "MsgPackReadUInt8 is given #Null")
AssertEqualsQ(MsgPackReadInt64(#Null), 0, "MsgPackReadInt64 is given #Null")
; The generic float reader shares the integer one's checks.
Assert(Bool(MsgPackReadFloat(#Null) = 0), "MsgPackReadFloat is given #Null")
; Ending
If FailedUnitTests
End 1
EndIf
Debug "Done !"
End 0
@@ -0,0 +1,194 @@
EnableExplicit
XIncludeFile "../_UnitTesting.pbi"
XIncludeFile "../../Includes/MessagePack.pbi"
XIncludeFile "../_TestBuffers.pbi"
; Writes `Value` with `MsgPackWriteInteger()` into a 1 byte buffer that has to grow,
; checks its first byte and size, and reads it back with `MsgPackReadInteger()`.
; For fixints, `ExpectedFirstByte` is the value itself.
Procedure CheckInteger(Value.q, ExpectedFirstByte.a, ExpectedSize.i)
Protected *MsgPack.MsgPackData = MsgPackCreate(1)
Protected Name.s = Str(Value)
AssertEquals(MsgPackWriteInteger(*MsgPack, Value), #MsgPack_Error_Success, "Wrote " + Name)
AssertEquals(*MsgPack\BufferOffset, ExpectedSize, Name + " takes " + Str(ExpectedSize) + " bytes")
AssertEquals(*MsgPack\BufferSize, ExpectedSize, Name + " grew the buffer exactly")
AssertEqualsA(PeekA(*MsgPack\Buffer), ExpectedFirstByte, Name + " first byte")
*MsgPack\BufferOffset = 0
AssertEqualsQ(MsgPackReadInteger(*MsgPack), Value, "Read back " + Name)
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_Success, "Read back " + Name + " without errors")
AssertEquals(*MsgPack\BufferOffset, ExpectedSize, "Read back " + Name + " moved the offset")
MsgPackFree(*MsgPack, #True)
EndProcedure
; Setup
Define *MsgPack.MsgPackData
Define QuadMin.q = -9223372036854775807 - 1
; Automatic format code selection, on each boundary
CheckInteger(0, $00, 1)
CheckInteger(1, $01, 1)
CheckInteger(127, $7F, 1)
CheckInteger(128, #MsgPack_FormatCode_UInt8, 2)
CheckInteger(255, #MsgPack_FormatCode_UInt8, 2)
CheckInteger(256, #MsgPack_FormatCode_UInt16, 3)
CheckInteger(65535, #MsgPack_FormatCode_UInt16, 3)
CheckInteger(65536, #MsgPack_FormatCode_UInt32, 5)
CheckInteger(4294967295, #MsgPack_FormatCode_UInt32, 5)
CheckInteger(4294967296, #MsgPack_FormatCode_UInt64, 9)
CheckInteger(9223372036854775807, #MsgPack_FormatCode_UInt64, 9)
CheckInteger(-1, $FF, 1)
CheckInteger(-32, $E0, 1)
CheckInteger(-33, #MsgPack_FormatCode_Int8, 2)
CheckInteger(-128, #MsgPack_FormatCode_Int8, 2)
CheckInteger(-129, #MsgPack_FormatCode_Int16, 3)
CheckInteger(-32768, #MsgPack_FormatCode_Int16, 3)
CheckInteger(-32769, #MsgPack_FormatCode_Int32, 5)
CheckInteger(-2147483648, #MsgPack_FormatCode_Int32, 5)
CheckInteger(-2147483649, #MsgPack_FormatCode_Int64, 9)
CheckInteger(QuadMin, #MsgPack_FormatCode_Int64, 9)
; Big-endian layouts
*MsgPack = MsgPackCreate(16)
MsgPackWriteInteger(*MsgPack, 4294967295)
MsgPackWriteInteger(*MsgPack, -129)
AssertEqualsA(PeekA(*MsgPack\Buffer + 1), $FF, "UInt32 byte 1")
AssertEqualsA(PeekA(*MsgPack\Buffer + 4), $FF, "UInt32 byte 4")
AssertEqualsA(PeekA(*MsgPack\Buffer + 5), #MsgPack_FormatCode_Int16, "Int16 format code")
AssertEqualsA(PeekA(*MsgPack\Buffer + 6), $FF, "Int16 high byte first")
AssertEqualsA(PeekA(*MsgPack\Buffer + 7), $7F, "Int16 low byte last")
MsgPackFree(*MsgPack, #True)
; Reading values written with forced format codes
*MsgPack = MsgPackCreate(1)
AssertEquals(MsgPackWriteUInt32(*MsgPack, $89ABCDEF), #MsgPack_Error_Success, "Wrote UInt32 with its high bit set")
AssertEquals(MsgPackWriteInt8(*MsgPack, -1), #MsgPack_Error_Success, "Wrote Int8")
AssertEquals(MsgPackWriteUInt8(*MsgPack, 200), #MsgPack_Error_Success, "Wrote UInt8")
AssertEquals(MsgPackWriteInt16(*MsgPack, 5), #MsgPack_Error_Success, "Wrote small Int16")
AssertEquals(MsgPackWriteUInt16(*MsgPack, $ABCD), #MsgPack_Error_Success, "Wrote UInt16")
AssertEquals(MsgPackWriteInt32(*MsgPack, -123456), #MsgPack_Error_Success, "Wrote Int32")
AssertEquals(MsgPackWriteInt64(*MsgPack, -2), #MsgPack_Error_Success, "Wrote small Int64")
AssertEquals(MsgPackWriteFixInt(*MsgPack, -5), #MsgPack_Error_Success, "Wrote negative fixint")
AssertEquals(MsgPackWriteFixInt(*MsgPack, 100), #MsgPack_Error_Success, "Wrote positive fixint")
AssertEquals(MsgPackWriteUInt8(*MsgPack, 42), #MsgPack_Error_Success, "Wrote trailing UInt8")
AssertEqualsA(PeekA(*MsgPack\Buffer + 5 + 2 + 2 + 3 + 3 + 5 + 9), $FB, "Negative fixint byte")
AssertEqualsA(PeekA(*MsgPack\Buffer + 5 + 2 + 2 + 3 + 3 + 5 + 9 + 1), $64, "Positive fixint byte")
*MsgPack\BufferOffset = 0
AssertEqualsQ(MsgPackReadInteger(*MsgPack), $89ABCDEF, "UInt32 is read as unsigned")
AssertEqualsQ(MsgPackReadInteger(*MsgPack), -1, "Int8 is read as signed")
AssertEqualsQ(MsgPackReadInteger(*MsgPack), 200, "UInt8 is read as unsigned")
AssertEqualsQ(MsgPackReadInteger(*MsgPack), 5, "Small Int16 is read")
AssertEqualsQ(MsgPackReadInteger(*MsgPack), $ABCD, "UInt16 is read as unsigned")
AssertEqualsQ(MsgPackReadInteger(*MsgPack), -123456, "Int32 is read as signed")
AssertEqualsQ(MsgPackReadInteger(*MsgPack), -2, "Small Int64 is read")
AssertEqualsQ(MsgPackReadInteger(*MsgPack), -5, "Negative fixint is read")
AssertEqualsQ(MsgPackReadInteger(*MsgPack), 100, "Positive fixint is read")
AssertEqualsA(MsgPackReadUInt8(*MsgPack), 42, "Read trailing UInt8")
AssertEquals(*MsgPack\BufferOffset, *MsgPack\BufferSize, "Offset is at the end")
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_Success, "No error after reading")
MsgPackFree(*MsgPack, #True)
; Known fixint vectors
*MsgPack = WrapHex("007F80E0FF")
AssertEqualsQ(MsgPackReadInteger(*MsgPack), 0, "0x00 is 0")
AssertEqualsQ(MsgPackReadInteger(*MsgPack), 127, "0x7F is 127")
AssertEqualsQ(MsgPackReadInteger(*MsgPack), 0, "0x80 is a fixmap, not an int")
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_InvalidFormatCode, "Fixmap error is set")
AssertEquals(*MsgPack\BufferOffset, 2, "Offset is untouched by the fixmap")
*MsgPack\BufferOffset = 3
AssertEqualsQ(MsgPackReadInteger(*MsgPack), -32, "0xE0 is -32")
AssertEqualsQ(MsgPackReadInteger(*MsgPack), -1, "0xFF is -1")
FreeWrappedHex(*MsgPack)
; Fixints out of range
*MsgPack = MsgPackCreate(8)
AssertEquals(MsgPackWriteFixInt(*MsgPack, 128), #MsgPack_Error_ValueOverflow, "128 isn't a fixint")
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_ValueOverflow, "Fixint overflow error is set")
AssertEquals(MsgPackWriteFixInt(*MsgPack, -33), #MsgPack_Error_ValueOverflow, "-33 isn't a fixint")
AssertEquals(MsgPackWriteFixInt(*MsgPack, 256 + 5), #MsgPack_Error_ValueOverflow, "261 isn't truncated into a fixint")
AssertEquals(*MsgPack\BufferOffset, 0, "Offset is untouched by rejected writes")
AssertEquals(MsgPackWriteFixInt(*MsgPack, 0), #MsgPack_Error_Success, "0 is a fixint")
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_Success, "Successful write cleared the error")
MsgPackFree(*MsgPack, #True)
; Buffers that can't grow
*MsgPack = WrapHex("0000")
AssertEquals(MsgPackWriteInteger(*MsgPack, 256), #MsgPack_Error_CannotGrowBufferDueToConfig, "UInt16's 3 bytes don't fit in 2")
AssertEquals(*MsgPack\BufferOffset, 0, "Offset is untouched by the failed write")
AssertEquals(MsgPackWriteInteger(*MsgPack, -100), #MsgPack_Error_Success, "Int8's 2 bytes fit exactly")
FreeWrappedHex(*MsgPack)
; Reading errors
*MsgPack = WrapHex("01")
*MsgPack\BufferOffset = 1
AssertEqualsQ(MsgPackReadInteger(*MsgPack), 0, "Read at the end fails")
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_AtOrPastTheEndOfBuffer, "Read at the end error is set")
FreeWrappedHex(*MsgPack)
*MsgPack = WrapHex("CA3F800000")
AssertEqualsQ(MsgPackReadInteger(*MsgPack), 0, "Reading a float as an int fails")
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_InvalidFormatCode, "Float as int error is set")
AssertEquals(*MsgPack\BufferOffset, 0, "Offset is untouched by the wrong format code")
FreeWrappedHex(*MsgPack)
*MsgPack = WrapHex("A161")
AssertEqualsQ(MsgPackReadInteger(*MsgPack), 0, "Reading a str as an int fails")
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_InvalidFormatCode, "Str as int error is set")
FreeWrappedHex(*MsgPack)
*MsgPack = WrapHex("CD00")
AssertEqualsQ(MsgPackReadInteger(*MsgPack), 0, "UInt16 with half its value fails")
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_BufferTooSmall, "Truncated UInt16 error is set")
AssertEquals(*MsgPack\BufferOffset, 0, "Offset is untouched by the truncated value")
FreeWrappedHex(*MsgPack)
*MsgPack = WrapHex("D1")
AssertEqualsQ(MsgPackReadInteger(*MsgPack), 0, "Int16 without its value fails")
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_BufferTooSmall, "Truncated Int16 error is set")
FreeWrappedHex(*MsgPack)
; UInt64 values that don't fit in a signed quad
*MsgPack = WrapHex("CF7FFFFFFFFFFFFFFF")
AssertEqualsQ(MsgPackReadInteger(*MsgPack), 9223372036854775807, "UInt64 (2^63)-1 fits")
FreeWrappedHex(*MsgPack)
*MsgPack = WrapHex("CF8000000000000000")
AssertEqualsQ(MsgPackReadInteger(*MsgPack), 0, "UInt64 2^63 doesn't fit")
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_ValueOverflow, "UInt64 overflow error is set")
AssertEquals(*MsgPack\BufferOffset, 0, "Offset is untouched by the overflow")
AssertEqualsQ(MsgPackReadUInt64(*MsgPack), QuadMin, "It can still be read as raw bits with MsgPackReadUInt64")
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_Success, "Raw read is a success")
FreeWrappedHex(*MsgPack)
*MsgPack = WrapHex("CF80")
AssertEqualsQ(MsgPackReadInteger(*MsgPack), 0, "Truncated UInt64 with its high bit set fails")
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_BufferTooSmall, "Truncation is reported over the overflow")
FreeWrappedHex(*MsgPack)
; Ending
If FailedUnitTests
End 1
EndIf
Debug "Done !"
End 0
+58
View File
@@ -0,0 +1,58 @@
EnableExplicit
XIncludeFile "../_UnitTesting.pbi"
XIncludeFile "../../Includes/MessagePack.pbi"
XIncludeFile "../_TestBuffers.pbi"
; Setup
Define *MsgPack.MsgPackData = MsgPackCreate(1)
; Reading both float sizes
AssertEquals(MsgPackWriteFloat32(*MsgPack, 1.5), #MsgPack_Error_Success, "Wrote Float32")
AssertEquals(MsgPackWriteFloat64(*MsgPack, -2.25), #MsgPack_Error_Success, "Wrote Float64")
AssertEquals(MsgPackWriteFloat32(*MsgPack, 0.1), #MsgPack_Error_Success, "Wrote inexact Float32")
AssertEquals(MsgPackWriteUInt8(*MsgPack, 42), #MsgPack_Error_Success, "Wrote trailing UInt8")
*MsgPack\BufferOffset = 0
Assert(Bool(MsgPackReadFloat(*MsgPack) = 1.5), "Float32 is read")
AssertEquals(*MsgPack\BufferOffset, 5, "Float32 moved the offset by 5")
Assert(Bool(MsgPackReadFloat(*MsgPack) = -2.25), "Float64 is read")
AssertEquals(*MsgPack\BufferOffset, 5 + 9, "Float64 moved the offset by 9")
; 0.1 isn't exact as a float32, so it must match the float32 value, not the double one.
Assert(Bool(MsgPackReadFloat(*MsgPack) = ValF("0.1")), "Inexact Float32 is widened as-is")
AssertEqualsA(MsgPackReadUInt8(*MsgPack), 42, "Read trailing UInt8")
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_Success, "No error after reading")
MsgPackFree(*MsgPack, #True)
; Reading errors
*MsgPack = WrapHex("CA")
*MsgPack\BufferOffset = 1
Assert(Bool(MsgPackReadFloat(*MsgPack) = 0), "Read at the end fails")
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_AtOrPastTheEndOfBuffer, "Read at the end error is set")
FreeWrappedHex(*MsgPack)
*MsgPack = WrapHex("01")
Assert(Bool(MsgPackReadFloat(*MsgPack) = 0), "Reading an int as a float fails")
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_InvalidFormatCode, "Int as float error is set")
AssertEquals(*MsgPack\BufferOffset, 0, "Offset is untouched by the wrong format code")
FreeWrappedHex(*MsgPack)
*MsgPack = WrapHex("CB3FF0000000")
Assert(Bool(MsgPackReadFloat(*MsgPack) = 0), "Truncated Float64 fails")
AssertEquals(MsgPackLastError(*MsgPack), #MsgPack_Error_BufferTooSmall, "Truncated Float64 error is set")
AssertEquals(*MsgPack\BufferOffset, 0, "Offset is untouched by the truncated value")
FreeWrappedHex(*MsgPack)
; Ending
If FailedUnitTests
End 1
EndIf
Debug "Done !"
End 0