mirror of
https://github.com/aziascreations/PB-MessagePack.git
synced 2026-11-03 17:01:24 +01:00
Added binary read/write functions, still untested
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -80,8 +80,12 @@ PureBasic include for creating/reading MessagePack data.
|
||||
* `#MsgPack_Error_AtOrPastTheEndOfBuffer`
|
||||
* `#MsgPack_Error_ReadPastEndOfBuffer`
|
||||
* `#MsgPack_Error_InvalidFormatCode`
|
||||
* `#MsgPack_Error_FailedToAllocateBuffer`
|
||||
* `#MsgPack_Error_BinaryDataSizeIsNegative`
|
||||
* `#MsgPack_Error_BinaryDataSizeIsTooBig`
|
||||
* `#MsgPack_Error_OutputBufferTooSmall`
|
||||
* `#MsgPack_Error_UnsupportedStringEncoding`
|
||||
* `#MsgPack_Error_InvalidStringSize`
|
||||
* `#MsgPack_Error_CannotGrowBufferDueToConfig`
|
||||
|
||||
* `.MsgPack_DataFlag` \
|
||||
@@ -127,6 +131,42 @@ PureBasic include for creating/reading MessagePack data.
|
||||
Returns `#MsgPack_Error_Success` on success, something else otherwise.
|
||||
|
||||
|
||||
### Binary
|
||||
* `MsgPackWriteBinary.MsgPack_ErrorCode(*MsgPackData.MsgPackData, *Buffer, BufferSize.q)` \
|
||||
Writes `BufferSize` bytes from `*Buffer` using the smallest bin format code that fits. \
|
||||
Returns `#MsgPack_Error_Success` on success, something else otherwise.
|
||||
|
||||
* `MsgPackWriteBin8/16/32.MsgPack_ErrorCode(*MsgPackData.MsgPackData, *Buffer, BufferSize.q)` \
|
||||
Same as `MsgPackWriteBinary`, but forces the given format code, even if a smaller one fits.
|
||||
|
||||
* `MsgPackReadBinary.q(*MsgPackData.MsgPackData, *OutputBuffer, OutputBufferSize.q)` \
|
||||
Copies the bin field at the current internal offset into `*OutputBuffer`. \
|
||||
Returns the number of bytes copied, or `#MsgPack_InvalidSize` on errors. \
|
||||
The offset isn't moved on errors, so it can be retried with a bigger buffer. \
|
||||
See: `MsgPackLastError`
|
||||
|
||||
* `MsgPackCopyBinary.i(*MsgPackData.MsgPackData)` \
|
||||
Copies the bin field at the current internal offset into a new buffer that must be freed with `FreeMemory()`. \
|
||||
Returns `#Null` on errors, or for empty bin fields with `#MsgPack_Error_Success` as the last error. \
|
||||
See: `MsgPackLastError`
|
||||
|
||||
|
||||
### Strings
|
||||
Supported encodings: `#PB_UTF8` (default, per spec), `#PB_Ascii`, `#PB_Unicode` (UTF-16 LE).
|
||||
|
||||
* `MsgPackWriteString.MsgPack_ErrorCode(*MsgPackData.MsgPackData, String.s, Encoding.i = #PB_UTF8)` \
|
||||
Writes `String` using the smallest str format code that fits its encoded byte size. \
|
||||
Returns `#MsgPack_Error_Success` on success, something else otherwise.
|
||||
|
||||
* `MsgPackWriteFixStr/Str8/Str16/Str32.MsgPack_ErrorCode(*MsgPackData.MsgPackData, String.s, Encoding.i = #PB_UTF8)` \
|
||||
Same as `MsgPackWriteString`, but forces the given format code.
|
||||
|
||||
* `MsgPackReadString.s(*MsgPackData.MsgPackData, Encoding.i = #PB_UTF8)` \
|
||||
Reads any str field at the current internal offset. \
|
||||
Returns `""` on errors and sets the internal error code. \
|
||||
See: `MsgPackLastError`
|
||||
|
||||
|
||||
## Remarks
|
||||
* Never touch the `MsgPackData` structure unless you **absolutely** have to.
|
||||
|
||||
|
||||
+237
-144
@@ -9,7 +9,7 @@
|
||||
; * //PureBasic 5.73 LTS (x86/x64)
|
||||
; * //PureBasic 6.21 (x86/x64)
|
||||
; * //PureBasic 6.21 C Backend (arm64)
|
||||
;
|
||||
;
|
||||
; ==- Links & License -===========================
|
||||
; License: CC0 1.0 Universal (Public Domain)
|
||||
; GitHub: https://github.com/aziascreations/PB-MessagePack
|
||||
@@ -20,6 +20,14 @@
|
||||
|
||||
; Sizes are all unsigned 8/16/32, using Q fixes any potential issues !
|
||||
|
||||
; The "sized field" helpers below handle every `[FormatCode][Size][Data]` layout
|
||||
; shared by the str and bin families.
|
||||
; The `SizeFieldByteLength` parameter is the number of bytes used to store the size:
|
||||
; * `0` - Size is stored in the format code's lower 5 bits. (FixStr)
|
||||
; * `1` - Str8, Bin8
|
||||
; * `2` - Str16, Bin16
|
||||
; * `4` - Str32, Bin32
|
||||
|
||||
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
@@ -30,194 +38,279 @@ CompilerIf #PB_Compiler_IsMainFile
|
||||
CompilerEndIf
|
||||
|
||||
XIncludeFile "./_Commons.pbi"
|
||||
XIncludeFile "./_Utils.pbi"
|
||||
XIncludeFile "./_Allocators.pbi"
|
||||
|
||||
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
;- Constants
|
||||
|
||||
#MsgPack_Binary_InvalidSize = -1
|
||||
XIncludeFile "./_Primitives.pbi"
|
||||
|
||||
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
;- Procedures
|
||||
|
||||
;-> Utils
|
||||
;-> Internal helpers
|
||||
|
||||
; Validates `DataSize`, reserves space for the whole field, writes its format code
|
||||
; and size, and moves the offset right after them.
|
||||
; The caller is expected to write `DataSize` bytes of data and move the offset afterward.
|
||||
; Sets the structure's error code and returns it.
|
||||
Procedure.MsgPack_ErrorCode _MsgPackWriteSizedFieldHeader(*MsgPackData.MsgPackData, FormatCode.MsgPack_FormatCode, SizeFieldByteLength.a, DataSize.q)
|
||||
Protected MaxDataSize.q
|
||||
Protected ErrorCode.MsgPack_ErrorCode
|
||||
|
||||
; TODO: Change this into a simplified global one for strings/maps/arrays/bin !
|
||||
Procedure.q _MsgPackGetSizeAfterFormatCode(*MsgPackData.MsgPackData)
|
||||
CompilerIf Not #MsgPack_DisableNullChecks
|
||||
If Not *MsgPackData
|
||||
DebuggerError("A #Null MsgPackData pointer was passed !")
|
||||
ProcedureReturn #MsgPack_Binary_InvalidSize
|
||||
ProcedureReturn #MsgPack_Error_NullPointerGiven
|
||||
EndIf
|
||||
CompilerEndIf
|
||||
|
||||
; FIXME: Not implemented !
|
||||
Select SizeFieldByteLength
|
||||
Case 0 :
|
||||
MaxDataSize = 31
|
||||
Case 1 :
|
||||
MaxDataSize = 255
|
||||
Case 2 :
|
||||
MaxDataSize = 65535
|
||||
Case 4 :
|
||||
MaxDataSize = 4294967295
|
||||
Default
|
||||
DebuggerError("Invalid size field length: " + Str(SizeFieldByteLength))
|
||||
*MsgPackData\LastError = #MsgPack_Error_InvalidValue
|
||||
ProcedureReturn #MsgPack_Error_InvalidValue
|
||||
EndSelect
|
||||
|
||||
If DataSize < 0
|
||||
DebuggerWarning("Received negative size !")
|
||||
*MsgPackData\LastError = #MsgPack_Error_BinaryDataSizeIsNegative
|
||||
ProcedureReturn #MsgPack_Error_BinaryDataSizeIsNegative
|
||||
ElseIf DataSize > MaxDataSize
|
||||
DebuggerWarning("Data is bigger than `" + Str(MaxDataSize) + "` bytes !")
|
||||
*MsgPackData\LastError = #MsgPack_Error_BinaryDataSizeIsTooBig
|
||||
ProcedureReturn #MsgPack_Error_BinaryDataSizeIsTooBig
|
||||
EndIf
|
||||
|
||||
ErrorCode = _MsgPackReserveWrite(*MsgPackData, 1 + SizeFieldByteLength + DataSize)
|
||||
If ErrorCode <> #MsgPack_Error_Success
|
||||
ProcedureReturn ErrorCode
|
||||
EndIf
|
||||
|
||||
Select SizeFieldByteLength
|
||||
Case 0
|
||||
PokeA(*MsgPackData\Buffer + *MsgPackData\BufferOffset, FormatCode | DataSize)
|
||||
Case 1
|
||||
PokeA(*MsgPackData\Buffer + *MsgPackData\BufferOffset, FormatCode)
|
||||
PokeA(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1, DataSize)
|
||||
Case 2
|
||||
PokeA(*MsgPackData\Buffer + *MsgPackData\BufferOffset, FormatCode)
|
||||
PokeU(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1, EndianSwapU16(DataSize))
|
||||
Case 4
|
||||
PokeA(*MsgPackData\Buffer + *MsgPackData\BufferOffset, FormatCode)
|
||||
PokeL(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1, EndianSwapI32(DataSize))
|
||||
EndSelect
|
||||
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + 1 + SizeFieldByteLength
|
||||
|
||||
ProcedureReturn #MsgPack_Error_Success
|
||||
EndProcedure
|
||||
|
||||
|
||||
; Writes a full `[FormatCode][Size][Data]` field by copying `DataSize` bytes from `*Data`.
|
||||
; Sets the structure's error code and returns it.
|
||||
Procedure.MsgPack_ErrorCode _MsgPackWriteSizedField(*MsgPackData.MsgPackData, FormatCode.MsgPack_FormatCode, SizeFieldByteLength.a, *Data, DataSize.q)
|
||||
Protected ErrorCode.MsgPack_ErrorCode
|
||||
|
||||
ErrorCode = _MsgPackWriteSizedFieldHeader(*MsgPackData, FormatCode, SizeFieldByteLength, DataSize)
|
||||
If ErrorCode <> #MsgPack_Error_Success
|
||||
ProcedureReturn ErrorCode
|
||||
EndIf
|
||||
|
||||
If DataSize > 0
|
||||
CopyMemory(*Data, *MsgPackData\Buffer + *MsgPackData\BufferOffset, DataSize)
|
||||
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + DataSize
|
||||
EndIf
|
||||
|
||||
ProcedureReturn #MsgPack_Error_Success
|
||||
EndProcedure
|
||||
|
||||
|
||||
; Reads the header of a str or bin field at the current offset, without moving it.
|
||||
; `ExpectedDataType` must be `#MsgPack_DataTypes_String` or `#MsgPack_DataTypes_Binary`.
|
||||
; On success, `*HeaderSize` receives the format code + size field length, and `*DataSize`
|
||||
; the data's length, both guaranteed to be within the buffer.
|
||||
; Sets the structure's error code and returns it.
|
||||
Procedure.MsgPack_ErrorCode _MsgPackReadSizedFieldHeader(*MsgPackData.MsgPackData, ExpectedDataType.MsgPack_DataType, *HeaderSize.Integer, *DataSize.Quad)
|
||||
Protected FormatCode.MsgPack_FormatCode
|
||||
Protected SizeFieldByteLength.a
|
||||
Protected DataSize.q
|
||||
|
||||
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)
|
||||
If MsgPackFormatCodeToDataType(FormatCode) <> ExpectedDataType
|
||||
Debug("Unexpected format code ! (" + RSet(Hex(PeekA(*MsgPackData\Buffer + *MsgPackData\BufferOffset)), 2, "0") + ")")
|
||||
*MsgPackData\LastError = #MsgPack_Error_InvalidFormatCode
|
||||
ProcedureReturn #MsgPack_Error_InvalidFormatCode
|
||||
EndIf
|
||||
|
||||
Select FormatCode
|
||||
Case #MsgPack_FormatCode_FixStr:
|
||||
SizeFieldByteLength = 0
|
||||
Case #MsgPack_FormatCode_Str8,
|
||||
#MsgPack_FormatCode_Bin8:
|
||||
SizeFieldByteLength = 1
|
||||
Case #MsgPack_FormatCode_Str16,
|
||||
#MsgPack_FormatCode_Bin16:
|
||||
SizeFieldByteLength = 2
|
||||
Case #MsgPack_FormatCode_Str32,
|
||||
#MsgPack_FormatCode_Bin32:
|
||||
SizeFieldByteLength = 4
|
||||
EndSelect
|
||||
|
||||
If Not _MsgPackHasSpaceLeft(*MsgPackData, 1 + SizeFieldByteLength)
|
||||
Debug("End of buffer reached, cannot read the field's size !")
|
||||
*MsgPackData\LastError = #MsgPack_Error_BufferTooSmall
|
||||
ProcedureReturn #MsgPack_Error_BufferTooSmall
|
||||
EndIf
|
||||
|
||||
Select SizeFieldByteLength
|
||||
Case 0:
|
||||
DataSize = PeekA(*MsgPackData\Buffer + *MsgPackData\BufferOffset) & %00011111
|
||||
Case 1:
|
||||
DataSize = PeekA(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1)
|
||||
Case 2:
|
||||
DataSize = EndianSwapU16(PeekU(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1))
|
||||
Case 4:
|
||||
DataSize = EndianSwapI32(PeekL(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1)) & $FFFFFFFF
|
||||
EndSelect
|
||||
|
||||
If Not _MsgPackHasSpaceLeft(*MsgPackData, 1 + SizeFieldByteLength + DataSize)
|
||||
Debug("End of buffer reached, cannot read the field's data !")
|
||||
*MsgPackData\LastError = #MsgPack_Error_BufferTooSmall
|
||||
ProcedureReturn #MsgPack_Error_BufferTooSmall
|
||||
EndIf
|
||||
|
||||
*HeaderSize\i = 1 + SizeFieldByteLength
|
||||
*DataSize\q = DataSize
|
||||
|
||||
*MsgPackData\LastError = #MsgPack_Error_Success
|
||||
ProcedureReturn #MsgPack_Error_Success
|
||||
EndProcedure
|
||||
|
||||
|
||||
|
||||
;-> Getters
|
||||
|
||||
Procedure.i MsgPackCopyBinary(*MsgPackData.MsgPackData)
|
||||
; Copies the bin field at the current offset into `*OutputBuffer`.
|
||||
; Returns: The number of bytes copied.
|
||||
; OnError: Returns `#MsgPack_InvalidSize` and sets the `MsgPackData\LastError` field.
|
||||
; The offset is left untouched, so a bigger buffer can be given afterward.
|
||||
Procedure.q MsgPackReadBinary(*MsgPackData.MsgPackData, *OutputBuffer, OutputBufferSize.q)
|
||||
Protected HeaderSize.i
|
||||
Protected DataSize.q
|
||||
|
||||
If _MsgPackReadSizedFieldHeader(*MsgPackData, #MsgPack_DataTypes_Binary, @HeaderSize, @DataSize) <> #MsgPack_Error_Success
|
||||
ProcedureReturn #MsgPack_InvalidSize
|
||||
EndIf
|
||||
|
||||
If DataSize > OutputBufferSize
|
||||
Debug("Output buffer is too small ! (" + Str(OutputBufferSize) + " < " + Str(DataSize) + ")")
|
||||
*MsgPackData\LastError = #MsgPack_Error_OutputBufferTooSmall
|
||||
ProcedureReturn #MsgPack_InvalidSize
|
||||
EndIf
|
||||
|
||||
If DataSize > 0
|
||||
CompilerIf Not #MsgPack_DisableNullChecks
|
||||
If Not *OutputBuffer
|
||||
DebuggerError("A #Null output buffer was passed !")
|
||||
*MsgPackData\LastError = #MsgPack_Error_NullPointerGiven
|
||||
ProcedureReturn #MsgPack_InvalidSize
|
||||
EndIf
|
||||
CompilerEndIf
|
||||
|
||||
CopyMemory(*MsgPackData\Buffer + *MsgPackData\BufferOffset + HeaderSize, *OutputBuffer, DataSize)
|
||||
EndIf
|
||||
|
||||
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + HeaderSize + DataSize
|
||||
|
||||
ProcedureReturn DataSize
|
||||
EndProcedure
|
||||
|
||||
Procedure.i MsgPackReadBinary(*MsgPackData.MsgPackData, *OutputBuffer, OutputBufferSize.q)
|
||||
|
||||
; Copies the bin field at the current offset into a new buffer.
|
||||
; Returns: A buffer from `AllocateMemory()` that the caller must free with `FreeMemory()`.
|
||||
; Its size can be obtained with `MemorySize()`.
|
||||
; Empty bin fields return `#Null` with `MsgPackData\LastError` set to `#MsgPack_Error_Success`.
|
||||
; OnError: Returns `#Null` and sets the `MsgPackData\LastError` field.
|
||||
Procedure.i MsgPackCopyBinary(*MsgPackData.MsgPackData)
|
||||
Protected HeaderSize.i
|
||||
Protected DataSize.q
|
||||
Protected *ReturnedValue = #Null
|
||||
|
||||
If _MsgPackReadSizedFieldHeader(*MsgPackData, #MsgPack_DataTypes_Binary, @HeaderSize, @DataSize) <> #MsgPack_Error_Success
|
||||
ProcedureReturn #Null
|
||||
EndIf
|
||||
|
||||
If DataSize > 0
|
||||
*ReturnedValue = AllocateMemory(DataSize, #PB_Memory_NoClear)
|
||||
If Not *ReturnedValue
|
||||
Debug("Failed to allocate memory for the copied binary !")
|
||||
*MsgPackData\LastError = #MsgPack_Error_FailedToAllocateBuffer
|
||||
ProcedureReturn #Null
|
||||
EndIf
|
||||
|
||||
CopyMemory(*MsgPackData\Buffer + *MsgPackData\BufferOffset + HeaderSize, *ReturnedValue, DataSize)
|
||||
EndIf
|
||||
|
||||
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + HeaderSize + DataSize
|
||||
|
||||
ProcedureReturn *ReturnedValue
|
||||
EndProcedure
|
||||
|
||||
|
||||
|
||||
;-> Setters
|
||||
|
||||
; Writes `BufferSize` bytes from `*Buffer` as a bin8.
|
||||
; Returns `#MsgPack_Error_Success` on success, something else otherwise.
|
||||
Procedure.MsgPack_ErrorCode MsgPackWriteBin8(*MsgPackData.MsgPackData, *Buffer, BufferSize.q)
|
||||
CompilerIf Not #MsgPack_DisableNullChecks
|
||||
If Not *MsgPackData
|
||||
DebuggerError("A #Null MsgPackData pointer was passed !")
|
||||
ProcedureReturn #MsgPack_Error_NullPointerGiven
|
||||
EndIf
|
||||
CompilerEndIf
|
||||
|
||||
*MsgPackData\LastError = #MsgPack_Error_Success
|
||||
|
||||
If BufferSize < 0
|
||||
DebuggerWarning("Received negative size !")
|
||||
*MsgPackData\LastError = #MsgPack_Error_BinaryDataSizeIsNegative
|
||||
ProcedureReturn *MsgPackData\LastError
|
||||
ElseIf BufferSize > 255
|
||||
DebuggerWarning("Binary buffer is bigger than `(2^8)-1` bytes !")
|
||||
*MsgPackData\LastError = #MsgPack_Error_BinaryDataSizeIsTooBig
|
||||
ProcedureReturn *MsgPackData\LastError
|
||||
EndIf
|
||||
|
||||
If Not _MsgPackHasSpaceLeft(*MsgPackData, BufferSize + 2)
|
||||
Protected GrowthErrorCode.MsgPack_ErrorCode
|
||||
|
||||
Debug("Growing buffer by `" + Str(BufferSize + 2) + "` bytes for bin8...")
|
||||
|
||||
GrowthErrorCode = MsgPackGrow(*MsgPackData, BufferSize + 2)
|
||||
If GrowthErrorCode <> #MsgPack_Error_Success
|
||||
; The structure's error code field is set by the called function !
|
||||
ProcedureReturn GrowthErrorCode
|
||||
EndIf
|
||||
EndIf
|
||||
|
||||
PokeA(*MsgPackData\Buffer + *MsgPackData\BufferOffset, #MsgPack_FormatCode_Bin8)
|
||||
PokeA(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1, BufferSize)
|
||||
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + 2
|
||||
|
||||
CopyMemory(*Buffer, *MsgPackData\Buffer + *MsgPackData\BufferOffset, BufferSize)
|
||||
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + BufferSize
|
||||
|
||||
ProcedureReturn #MsgPack_Error_Success
|
||||
ProcedureReturn _MsgPackWriteSizedField(*MsgPackData, #MsgPack_FormatCode_Bin8, 1, *Buffer, BufferSize)
|
||||
EndProcedure
|
||||
|
||||
|
||||
; Writes `BufferSize` bytes from `*Buffer` as a bin16, even if a smaller format code fits.
|
||||
; Returns `#MsgPack_Error_Success` on success, something else otherwise.
|
||||
Procedure.MsgPack_ErrorCode MsgPackWriteBin16(*MsgPackData.MsgPackData, *Buffer, BufferSize.q)
|
||||
CompilerIf Not #MsgPack_DisableNullChecks
|
||||
If Not *MsgPackData
|
||||
DebuggerError("A #Null MsgPackData pointer was passed !")
|
||||
ProcedureReturn #MsgPack_Error_NullPointerGiven
|
||||
EndIf
|
||||
CompilerEndIf
|
||||
|
||||
*MsgPackData\LastError = #MsgPack_Error_Success
|
||||
|
||||
If BufferSize < 0
|
||||
DebuggerWarning("Received negative size !")
|
||||
*MsgPackData\LastError = #MsgPack_Error_BinaryDataSizeIsNegative
|
||||
ProcedureReturn *MsgPackData\LastError
|
||||
ElseIf BufferSize > 65535
|
||||
DebuggerWarning("Binary buffer is bigger than `(2^16)-1` bytes !")
|
||||
*MsgPackData\LastError = #MsgPack_Error_BinaryDataSizeIsTooBig
|
||||
ProcedureReturn *MsgPackData\LastError
|
||||
EndIf
|
||||
|
||||
If Not _MsgPackHasSpaceLeft(*MsgPackData, BufferSize + 3)
|
||||
Protected GrowthErrorCode.MsgPack_ErrorCode
|
||||
|
||||
Debug("Growing buffer by `" + Str(BufferSize + 3) + "` bytes for bin16...")
|
||||
|
||||
GrowthErrorCode = MsgPackGrow(*MsgPackData, BufferSize + 3)
|
||||
If GrowthErrorCode <> #MsgPack_Error_Success
|
||||
; The structure's error code field is set by the called function !
|
||||
ProcedureReturn GrowthErrorCode
|
||||
EndIf
|
||||
EndIf
|
||||
|
||||
PokeA(*MsgPackData\Buffer + *MsgPackData\BufferOffset, #MsgPack_FormatCode_Bin16)
|
||||
PokeU(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1, EndianSwapU(BufferSize))
|
||||
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + 3
|
||||
|
||||
CopyMemory(*Buffer, *MsgPackData\Buffer + *MsgPackData\BufferOffset, BufferSize)
|
||||
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + BufferSize
|
||||
|
||||
ProcedureReturn #MsgPack_Error_Success
|
||||
ProcedureReturn _MsgPackWriteSizedField(*MsgPackData, #MsgPack_FormatCode_Bin16, 2, *Buffer, BufferSize)
|
||||
EndProcedure
|
||||
|
||||
|
||||
; Writes `BufferSize` bytes from `*Buffer` as a bin32, even if a smaller format code fits.
|
||||
; Returns `#MsgPack_Error_Success` on success, something else otherwise.
|
||||
Procedure.MsgPack_ErrorCode MsgPackWriteBin32(*MsgPackData.MsgPackData, *Buffer, BufferSize.q)
|
||||
CompilerIf Not #MsgPack_DisableNullChecks
|
||||
If Not *MsgPackData
|
||||
DebuggerError("A #Null MsgPackData pointer was passed !")
|
||||
ProcedureReturn #MsgPack_Error_NullPointerGiven
|
||||
EndIf
|
||||
CompilerEndIf
|
||||
|
||||
*MsgPackData\LastError = #MsgPack_Error_Success
|
||||
|
||||
If BufferSize < 0
|
||||
DebuggerWarning("Received negative size !")
|
||||
*MsgPackData\LastError = #MsgPack_Error_BinaryDataSizeIsNegative
|
||||
ProcedureReturn *MsgPackData\LastError
|
||||
ElseIf BufferSize > 4294967295
|
||||
DebuggerWarning("Binary buffer is bigger than `(2^32)-1` bytes !")
|
||||
*MsgPackData\LastError = #MsgPack_Error_BinaryDataSizeIsTooBig
|
||||
ProcedureReturn *MsgPackData\LastError
|
||||
EndIf
|
||||
|
||||
If Not _MsgPackHasSpaceLeft(*MsgPackData, BufferSize + 5)
|
||||
Protected GrowthErrorCode.MsgPack_ErrorCode
|
||||
|
||||
Debug("Growing buffer by `" + Str(BufferSize + 5) + "` bytes for bin16...")
|
||||
|
||||
GrowthErrorCode = MsgPackGrow(*MsgPackData, BufferSize + 5)
|
||||
If GrowthErrorCode <> #MsgPack_Error_Success
|
||||
; The structure's error code field is set by the called function !
|
||||
ProcedureReturn GrowthErrorCode
|
||||
EndIf
|
||||
EndIf
|
||||
|
||||
PokeA(*MsgPackData\Buffer + *MsgPackData\BufferOffset, #MsgPack_FormatCode_Bin32)
|
||||
PokeI(*MsgPackData\Buffer + *MsgPackData\BufferOffset + 1, EndianSwapI(BufferSize))
|
||||
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + 5
|
||||
|
||||
CopyMemory(*Buffer, *MsgPackData\Buffer + *MsgPackData\BufferOffset, BufferSize)
|
||||
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + BufferSize
|
||||
|
||||
ProcedureReturn #MsgPack_Error_Success
|
||||
ProcedureReturn _MsgPackWriteSizedField(*MsgPackData, #MsgPack_FormatCode_Bin32, 4, *Buffer, BufferSize)
|
||||
EndProcedure
|
||||
|
||||
|
||||
; Writes `BufferSize` bytes from `*Buffer` using the smallest bin format code that fits.
|
||||
; Returns `#MsgPack_Error_Success` on success, something else otherwise.
|
||||
Procedure.MsgPack_ErrorCode MsgPackWriteBinary(*MsgPackData.MsgPackData, *Buffer, BufferSize.q)
|
||||
If BufferSize < 0
|
||||
DebuggerWarning("Received negative size !")
|
||||
*MsgPackData\LastError = #MsgPack_Error_BinaryDataSizeIsNegative
|
||||
ProcedureReturn *MsgPackData\LastError
|
||||
ElseIf BufferSize <= 255
|
||||
If BufferSize <= 255
|
||||
ProcedureReturn MsgPackWriteBin8(*MsgPackData, *Buffer, BufferSize)
|
||||
ElseIf BufferSize <= 65535
|
||||
ElseIf BufferSize <= 65535
|
||||
ProcedureReturn MsgPackWriteBin16(*MsgPackData, *Buffer, BufferSize)
|
||||
ElseIf BufferSize <= 4294967295
|
||||
Else
|
||||
; Sizes above `(2^32)-1` are rejected by the helper.
|
||||
ProcedureReturn MsgPackWriteBin32(*MsgPackData, *Buffer, BufferSize)
|
||||
Else
|
||||
DebuggerWarning("Binary buffer is bigger than `(2^32)-1` bytes !")
|
||||
*MsgPackData\LastError = #MsgPack_Error_BinaryDataSizeIsTooBig
|
||||
ProcedureReturn *MsgPackData\LastError
|
||||
EndIf
|
||||
EndIf
|
||||
EndProcedure
|
||||
|
||||
|
||||
@@ -146,10 +146,16 @@ Enumeration _MsgPack_ErrorCodes
|
||||
#MsgPack_Error_AtOrPastTheEndOfBuffer = 11
|
||||
#MsgPack_Error_ReadPastEndOfBuffer = 12
|
||||
#MsgPack_Error_InvalidFormatCode = 13
|
||||
#MsgPack_Error_FailedToAllocateBuffer = 14
|
||||
|
||||
; 5x - Binary
|
||||
#MsgPack_Error_BinaryDataSizeIsNegative = 50
|
||||
#MsgPack_Error_BinaryDataSizeIsTooBig = 51
|
||||
#MsgPack_Error_OutputBufferTooSmall = 52
|
||||
|
||||
; 6x - Strings
|
||||
#MsgPack_Error_UnsupportedStringEncoding = 60
|
||||
#MsgPack_Error_InvalidStringSize = 61
|
||||
|
||||
|
||||
#MsgPack_Error_CannotGrowBufferDueToConfig = 100
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
;{- Code Header
|
||||
; ==- Basic Info -================================
|
||||
; Name: _Extensions.pbi
|
||||
; Version: 0.0.0
|
||||
; Author: Herwin Bozet (NibblePoker)
|
||||
;
|
||||
; ==- Compatibility -=============================
|
||||
; Tested compiler version:
|
||||
; * //PureBasic 5.73 LTS (x86/x64)
|
||||
; * //PureBasic 6.21 (x86/x64)
|
||||
; * //PureBasic 6.21 C Backend (arm64)
|
||||
;
|
||||
; ==- Links & License -===========================
|
||||
; License: CC0 1.0 Universal (Public Domain)
|
||||
; GitHub: https://github.com/aziascreations/PB-MessagePack
|
||||
;}
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
;- Notes
|
||||
|
||||
; Specification:
|
||||
; * https://github.com/msgpack/msgpack/blob/master/spec.md
|
||||
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
;- Compiler directive
|
||||
|
||||
CompilerIf #PB_Compiler_IsMainFile
|
||||
EnableExplicit
|
||||
CompilerEndIf
|
||||
|
||||
XIncludeFile "./_Commons.pbi"
|
||||
|
||||
@@ -37,6 +37,7 @@ XIncludeFile "./_Allocators.pbi"
|
||||
; Ensures `Size` bytes can be written at the current offset, growing the buffer by
|
||||
; exactly the missing amount if needed.
|
||||
; Sets the structure's error code and returns it.
|
||||
; FIXME: Move elsewhere since it's used in multiple places !
|
||||
Procedure.MsgPack_ErrorCode _MsgPackReserveWrite(*MsgPackData.MsgPackData, Size.i)
|
||||
Protected MissingSize.i
|
||||
|
||||
|
||||
+169
-2
@@ -9,24 +9,191 @@
|
||||
; * //PureBasic 5.73 LTS (x86/x64)
|
||||
; * //PureBasic 6.21 (x86/x64)
|
||||
; * //PureBasic 6.21 C Backend (arm64)
|
||||
;
|
||||
;
|
||||
; ==- Links & License -===========================
|
||||
; License: CC0 1.0 Universal (Public Domain)
|
||||
; GitHub: https://github.com/aziascreations/PB-MessagePack
|
||||
;}
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
;- Notes
|
||||
|
||||
; The spec defines str as UTF-8, which is the default everywhere.
|
||||
; `#PB_Ascii` and `#PB_Unicode` (UTF-16 LE) are supported for peers that don't follow it.
|
||||
; Sizes are always in bytes of the encoded string, without any null terminator.
|
||||
; Strings containing a null character will be truncated on read, as with `PeekS()`.
|
||||
|
||||
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
;- Compiler directive
|
||||
|
||||
CompilerIf #PB_Compiler_IsMainFile
|
||||
EnableExplicit
|
||||
CompilerEndIf
|
||||
|
||||
XIncludeFile "./_Commons.pbi"
|
||||
XIncludeFile "./_Allocators.pbi"
|
||||
XIncludeFile "./_Binary.pbi"
|
||||
|
||||
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
;- Private macros
|
||||
|
||||
Macro _MsgPackIsSupportedStringEncoding(Encoding)
|
||||
(Encoding = #PB_UTF8 Or Encoding = #PB_Ascii Or Encoding = #PB_Unicode)
|
||||
EndMacro
|
||||
|
||||
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
;- Procedures
|
||||
|
||||
;-> Getters & Setters
|
||||
;-> Internal helpers
|
||||
|
||||
; Returns the byte length of `String` once encoded in `Encoding`, without a null terminator.
|
||||
; Sets the structure's error code and returns `#MsgPack_InvalidSize` on unsupported encodings.
|
||||
Procedure.q _MsgPackGetEncodedStringSize(*MsgPackData.MsgPackData, String.s, Encoding.i)
|
||||
CompilerIf Not #MsgPack_DisableNullChecks
|
||||
If Not *MsgPackData
|
||||
DebuggerError("A #Null MsgPackData pointer was passed !")
|
||||
ProcedureReturn #MsgPack_InvalidSize
|
||||
EndIf
|
||||
CompilerEndIf
|
||||
|
||||
If Not _MsgPackIsSupportedStringEncoding(Encoding)
|
||||
DebuggerError("Unsupported string encoding: " + Str(Encoding))
|
||||
*MsgPackData\LastError = #MsgPack_Error_UnsupportedStringEncoding
|
||||
ProcedureReturn #MsgPack_InvalidSize
|
||||
EndIf
|
||||
|
||||
ProcedureReturn StringByteLength(String, Encoding)
|
||||
EndProcedure
|
||||
|
||||
|
||||
; Writes a full str field whose encoded size is already known.
|
||||
; Sets the structure's error code and returns it.
|
||||
Procedure.MsgPack_ErrorCode _MsgPackWriteStringField(*MsgPackData.MsgPackData, FormatCode.MsgPack_FormatCode, SizeFieldByteLength.a, String.s, StringSize.q, Encoding.i)
|
||||
Protected ErrorCode.MsgPack_ErrorCode
|
||||
|
||||
ErrorCode = _MsgPackWriteSizedFieldHeader(*MsgPackData, FormatCode, SizeFieldByteLength, StringSize)
|
||||
If ErrorCode <> #MsgPack_Error_Success
|
||||
ProcedureReturn ErrorCode
|
||||
EndIf
|
||||
|
||||
If StringSize > 0
|
||||
PokeS(*MsgPackData\Buffer + *MsgPackData\BufferOffset, String, -1, Encoding | #PB_String_NoZero)
|
||||
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + StringSize
|
||||
EndIf
|
||||
|
||||
ProcedureReturn #MsgPack_Error_Success
|
||||
EndProcedure
|
||||
|
||||
|
||||
; Writes a str field with an explicit format code.
|
||||
; Sets the structure's error code and returns it.
|
||||
Procedure.MsgPack_ErrorCode _MsgPackWriteStringAs(*MsgPackData.MsgPackData, FormatCode.MsgPack_FormatCode, SizeFieldByteLength.a, String.s, Encoding.i)
|
||||
Protected StringSize.q
|
||||
|
||||
StringSize = _MsgPackGetEncodedStringSize(*MsgPackData, String, Encoding)
|
||||
If StringSize = #MsgPack_InvalidSize
|
||||
ProcedureReturn MsgPackLastError(*MsgPackData)
|
||||
EndIf
|
||||
|
||||
ProcedureReturn _MsgPackWriteStringField(*MsgPackData, FormatCode, SizeFieldByteLength, String, StringSize, Encoding)
|
||||
EndProcedure
|
||||
|
||||
|
||||
|
||||
;-> Getters
|
||||
|
||||
; Reads the str field at the current offset and decodes it from `Encoding`.
|
||||
; Returns: The decoded string.
|
||||
; OnError: Returns `""` and sets the `MsgPackData\LastError` field.
|
||||
Procedure.s MsgPackReadString(*MsgPackData.MsgPackData, Encoding.i = #PB_UTF8)
|
||||
Protected HeaderSize.i
|
||||
Protected StringSize.q
|
||||
Protected *StringStart
|
||||
Protected ReturnedValue.s = ""
|
||||
|
||||
If _MsgPackReadSizedFieldHeader(*MsgPackData, #MsgPack_DataTypes_String, @HeaderSize, @StringSize) <> #MsgPack_Error_Success
|
||||
ProcedureReturn ""
|
||||
EndIf
|
||||
|
||||
If Not _MsgPackIsSupportedStringEncoding(Encoding)
|
||||
DebuggerError("Unsupported string encoding: " + Str(Encoding))
|
||||
*MsgPackData\LastError = #MsgPack_Error_UnsupportedStringEncoding
|
||||
ProcedureReturn ""
|
||||
EndIf
|
||||
|
||||
If Encoding = #PB_Unicode And StringSize % 2 <> 0
|
||||
DebuggerWarning("UTF-16 string has an odd byte size !")
|
||||
*MsgPackData\LastError = #MsgPack_Error_InvalidStringSize
|
||||
ProcedureReturn ""
|
||||
EndIf
|
||||
|
||||
*StringStart = *MsgPackData\Buffer + *MsgPackData\BufferOffset + HeaderSize
|
||||
|
||||
If StringSize > 0
|
||||
Select Encoding
|
||||
Case #PB_UTF8
|
||||
ReturnedValue = PeekS(*StringStart, StringSize, #PB_UTF8 | #PB_ByteLength)
|
||||
Case #PB_Ascii
|
||||
ReturnedValue = PeekS(*StringStart, StringSize, #PB_Ascii)
|
||||
Case #PB_Unicode
|
||||
ReturnedValue = PeekS(*StringStart, StringSize / 2, #PB_Unicode)
|
||||
EndSelect
|
||||
EndIf
|
||||
|
||||
*MsgPackData\BufferOffset = *MsgPackData\BufferOffset + HeaderSize + StringSize
|
||||
|
||||
ProcedureReturn ReturnedValue
|
||||
EndProcedure
|
||||
|
||||
|
||||
|
||||
;-> Setters
|
||||
|
||||
; Writes `String` encoded as `Encoding` using the smallest str format code that fits.
|
||||
; Returns `#MsgPack_Error_Success` on success, something else otherwise.
|
||||
Procedure.MsgPack_ErrorCode MsgPackWriteString(*MsgPackData.MsgPackData, String.s, Encoding.i = #PB_UTF8)
|
||||
Protected StringSize.q
|
||||
|
||||
StringSize = _MsgPackGetEncodedStringSize(*MsgPackData, String, Encoding)
|
||||
If StringSize = #MsgPack_InvalidSize
|
||||
ProcedureReturn MsgPackLastError(*MsgPackData)
|
||||
EndIf
|
||||
|
||||
If StringSize <= 31
|
||||
ProcedureReturn _MsgPackWriteStringField(*MsgPackData, #MsgPack_FormatCode_FixStr, 0, String, StringSize, Encoding)
|
||||
ElseIf StringSize <= 255
|
||||
ProcedureReturn _MsgPackWriteStringField(*MsgPackData, #MsgPack_FormatCode_Str8, 1, String, StringSize, Encoding)
|
||||
ElseIf StringSize <= 65535
|
||||
ProcedureReturn _MsgPackWriteStringField(*MsgPackData, #MsgPack_FormatCode_Str16, 2, String, StringSize, Encoding)
|
||||
Else
|
||||
; Sizes above `(2^32)-1` are rejected by the helper.
|
||||
ProcedureReturn _MsgPackWriteStringField(*MsgPackData, #MsgPack_FormatCode_Str32, 4, String, StringSize, Encoding)
|
||||
EndIf
|
||||
EndProcedure
|
||||
|
||||
|
||||
|
||||
; ------------------------------------------------------------------------------
|
||||
;- Macros
|
||||
|
||||
Macro MsgPackWriteFixStr(MsgPackData, String, Encoding = #PB_UTF8)
|
||||
_MsgPackWriteStringAs(MsgPackData, #MsgPack_FormatCode_FixStr, 0, String, Encoding)
|
||||
EndMacro
|
||||
|
||||
Macro MsgPackWriteStr8(MsgPackData, String, Encoding = #PB_UTF8)
|
||||
_MsgPackWriteStringAs(MsgPackData, #MsgPack_FormatCode_Str8, 1, String, Encoding)
|
||||
EndMacro
|
||||
|
||||
Macro MsgPackWriteStr16(MsgPackData, String, Encoding = #PB_UTF8)
|
||||
_MsgPackWriteStringAs(MsgPackData, #MsgPack_FormatCode_Str16, 2, String, Encoding)
|
||||
EndMacro
|
||||
|
||||
Macro MsgPackWriteStr32(MsgPackData, String, Encoding = #PB_UTF8)
|
||||
_MsgPackWriteStringAs(MsgPackData, #MsgPack_FormatCode_Str32, 4, String, Encoding)
|
||||
EndMacro
|
||||
|
||||
Reference in New Issue
Block a user