; ------------------------------------------------------------------------------ ;- Notes ; This example shows you how to iterate over a MessagePack buffer and dump ; every field to the console with its general data type. ; Each field is read with the implicitly typed procedure of its data type, ; which accepts all of the type's format codes. ; Arrays and maps are dumped recursively, map entries key first. ; ------------------------------------------------------------------------------ ;- Example EnableExplicit XIncludeFile "../../Includes/MessagePack.pbi" ; Ends the program if a write failed. Procedure CheckWrite(ErrorCode.MsgPack_ErrorCode, What.s) If ErrorCode <> #MsgPack_Error_Success PrintN("Failed to write " + What + " ! (Error " + Str(ErrorCode) + ")") End 1 EndIf EndProcedure ; Ends the program if a structure or buffer couldn't be prepared. Procedure CheckPointer(*Pointer, What.s) If Not *Pointer PrintN("Failed to prepare " + What + " !") End 1 EndIf EndProcedure ; Returns the bytes of a buffer from `MsgPackCopyBinary/Extension` as hex. ; Empty bin and ext fields give a `#Null` buffer. Procedure.s HexDump(*Data) Protected Text.s = "" Protected i.i If Not *Data ProcedureReturn "" EndIf For i = 0 To MemorySize(*Data) - 1 Text = Text + RSet(Hex(PeekA(*Data + i)), 2, "0") + " " Next ProcedureReturn RTrim(Text) EndProcedure ; Prints the field at the current offset and moves past it. ; `Label` is printed before it, to tell array indexes and map keys/values apart. Procedure.b DumpField(*MsgPackData, Indent.s, Label.s) Protected DataType.MsgPack_DataType = MsgPackFormatCodeToDataType(MsgPackGetFormatCode(*MsgPackData)) Protected Text.s Protected *Iterator, *Data Protected Success.b Protected EntryCount.q, Index.q Select DataType Case #MsgPack_DataTypes_Null MsgPackReadNull(*MsgPackData) Text = "Null" Case #MsgPack_DataTypes_Boolean If MsgPackReadBoolean(*MsgPackData) Text = "Boolean: True" Else Text = "Boolean: False" EndIf Case #MsgPack_DataTypes_Integer ; UInt64 values above `(2^63)-1` fail here, see `MsgPackReadUInt64`. Text = "Integer: " + Str(MsgPackReadInteger(*MsgPackData)) Case #MsgPack_DataTypes_Float ; Float32 values are returned as a `.d`. Text = "Float: " + StrD(MsgPackReadFloat(*MsgPackData)) Case #MsgPack_DataTypes_String Text = "String: " + Chr(34) + MsgPackReadString(*MsgPackData) + Chr(34) Case #MsgPack_DataTypes_Binary *Data = MsgPackCopyBinary(*MsgPackData) Text = "Binary: " + HexDump(*Data) Case #MsgPack_DataTypes_Extension ; The type doesn't move the offset, the data is read right after. Text = "Extension (Type " + Str(MsgPackGetExtensionType(*MsgPackData)) + ")" If MsgPackLastError(*MsgPackData) <> #MsgPack_Error_Success ProcedureReturn #False EndIf *Data = MsgPackCopyExtension(*MsgPackData) Text = Text + ": " + HexDump(*Data) Case #MsgPack_DataTypes_Array, #MsgPack_DataTypes_Map EntryCount = MsgPackGetEntryCount(*MsgPackData) *Iterator = MsgPackExamine(*MsgPackData) If Not *Iterator ProcedureReturn #False EndIf If DataType = #MsgPack_DataTypes_Map PrintN(Indent + Label + "Map (" + Str(EntryCount) + " entries)") Else PrintN(Indent + Label + "Array (" + Str(EntryCount) + " entries)") EndIf Success = #True Index = 0 While MsgPackNextEntry(*Iterator) If DataType = #MsgPack_DataTypes_Map Success = DumpField(*Iterator, Indent + " ", "Key: ") If Success Success = DumpField(*Iterator, Indent + " ", "Value: ") EndIf Else Success = DumpField(*Iterator, Indent + " ", "[" + Str(Index) + "] ") EndIf If Not Success Break EndIf Index = Index + 1 Wend ; `MsgPackNextEntry` also returns `#False` on errors. If MsgPackLastError(*Iterator) <> #MsgPack_Error_Success Success = #False EndIf MsgPackFinish(*Iterator) ProcedureReturn Success Default PrintN(Indent + Label + "Unknown data type !") ProcedureReturn #False EndSelect If *Data FreeMemory(*Data) EndIf If MsgPackLastError(*MsgPackData) <> #MsgPack_Error_Success ProcedureReturn #False EndIf PrintN(Indent + Label + Text) ProcedureReturn #True EndProcedure ; Obscure structures that hold info about the MsgPack data Define *Writer, *Entries, *Fields, *Reader ; MsgPack-encoded buffer that will get exported midway in this example. Define *Buffer ; Raw data for the bin and ext fields. Define Bytes.l = $44332211 Define FieldSize.q, BytesLeft.q OpenConsole() ; Writing a bit of everything, the smallest format code that fits is used. *Writer = MsgPackCreate(64) CheckPointer(*Writer, "the writer") CheckWrite(MsgPackWriteNull(*Writer), "a nil") CheckWrite(MsgPackWriteBoolean(*Writer, #True), "a boolean") CheckWrite(MsgPackWriteBoolean(*Writer, #False), "a boolean") CheckWrite(MsgPackWriteInteger(*Writer, 42), "an integer") CheckWrite(MsgPackWriteInteger(*Writer, -5), "an integer") CheckWrite(MsgPackWriteInteger(*Writer, 200), "an integer") CheckWrite(MsgPackWriteInteger(*Writer, 70000), "an integer") CheckWrite(MsgPackWriteInteger(*Writer, -1000), "an integer") ; Floats don't have an implicitly typed setter. CheckWrite(MsgPackWriteFloat64(*Writer, 3.14159), "a float") CheckWrite(MsgPackWriteString(*Writer, "Hello"), "a string") CheckWrite(MsgPackWriteBinary(*Writer, @Bytes, 4), "a binary") CheckWrite(MsgPackWriteExtension(*Writer, 1, @Bytes, 4), "an extension") CheckWrite(MsgPackWriteExtension(*Writer, 2, @Bytes, 3), "an extension") ; Writing the entries of an array in their own writer. *Entries = MsgPackCreate(16) CheckPointer(*Entries, "the entries writer") CheckWrite(MsgPackWriteInteger(*Entries, 10), "an entry") CheckWrite(MsgPackWriteInteger(*Entries, 20), "an entry") CheckWrite(MsgPackWriteInteger(*Entries, 30), "an entry") ; Writing the keys and values of a map, with the array as a value. *Fields = MsgPackCreate(32) CheckPointer(*Fields, "the fields writer") CheckWrite(MsgPackWriteString(*Fields, "Name"), "a key") CheckWrite(MsgPackWriteString(*Fields, "Herwin"), "a value") CheckWrite(MsgPackWriteString(*Fields, "Scores"), "a key") CheckWrite(MsgPackWriteArrayFrom(*Fields, *Entries), "an array") CheckWrite(MsgPackWriteMapFrom(*Writer, *Fields), "a map") ; The entries and fields writers are left untouched, so they must be freed. MsgPackFree(*Entries, #True) MsgPackFree(*Fields, #True) ; Exporting the data and giving it to a reader. *Buffer = MsgPackFinalize(*Writer) CheckPointer(*Buffer, "the final buffer") PrintN("Encoded size: " + Str(MemorySize(*Buffer))) PrintN("") *Reader = MsgPackWrapMsgBuffer(*Buffer, MemorySize(*Buffer), #True) CheckPointer(*Reader, "the reader") ; Dumping every top-level field until the whole buffer is read. ; Each field's full size is checked first, which also validates it. BytesLeft = MemorySize(*Buffer) While BytesLeft > 0 FieldSize = MsgPackGetFieldFullSize(*Reader) If FieldSize = #MsgPack_InvalidSize PrintN("Invalid field ! (Error " + Str(MsgPackLastError(*Reader)) + ")") End 2 EndIf If Not DumpField(*Reader, "", "") PrintN("Failed to dump a field !") End 3 EndIf BytesLeft = BytesLeft - FieldSize Wend ; Freeing the obscure structure and the buffer it owns. MsgPackFree(*Reader, #True) PrintN("") PrintN("Press enter to exit.") Input() End 0