# Data Format GraphEncoder encode data in a private binary format. Having to preserve information about the types, GraphCodable creates larger data in bytes than those generated by Codable encoders. Despite this, the time required for encoding and decoding is generally comparable if not faster. You can get a readable representation of the encoded data in a string with the `dump()` function. ## Readable output of the encoded data Let's consider this example: ```swift import Foundation import GraphCodable struct AStruct : Equatable, GCodable { var array = [1,2,3] var dict = ["4":4,"5":5] init() {} private enum Key : String { case array, dict } func encode(to encoder: GEncoder) throws { try encoder.encode( array, for: Key.array ) try encoder.encode( dict, for: Key.dict ) } init(from decoder: GDecoder) throws { array = try decoder.decode(for: Key.array ) dict = try decoder.decode(for: Key.dict ) } } class AClass : GCodable { var astruct = AStruct() var aclass : AClass? private enum Key : String { case astruct, aclass } init() {} func encode(to encoder: GEncoder) throws { try encoder.encode( astruct, for: Key.astruct ) // note: try encoder.encodeConditional( aclass, for: Key.aclass ) } required init(from decoder: GDecoder) throws { astruct = try decoder.decode(for: Key.astruct ) aclass = try decoder.decode(for: Key.aclass ) } } let a = AClass() let b = AClass() // we store a in b.aclass while a.aclass remains nil b.aclass = a let inRoot = [b, b, b, b, a] print( try GraphEncoder().dump( inRoot ) ) ``` The result: ``` == GRAPH ========================================================= - VAL - REF1000 MyGraphCodableApp.AClass + "astruct": VAL + "array": [1, 2, 3] + "dict": ["4": 4, "5": 5] . + "aclass": PTR1001? . - PTR1000 - PTR1000 - PTR1000 - REF1001 MyGraphCodableApp.AClass + "astruct": VAL + "array": [1, 2, 3] + "dict": ["4": 4, "5": 5] . + "aclass": nil . . ================================================================== ``` By default `dump()` prints only the **BODY** section of the data. The **BODY** section contains the structured data, organized in: - *Sequences*: a list of items preceded by the **-** symbol. - *Dictionaries*: a list of items (in the form "key": value) preceded by the **+** symbol. Both lists end when the symbol **.** is encountered. The root is always the only item of the first sequence. Rows by rows: - `VAL`, a value type, is the root. - It contains 5 elements corresponding to `[b, b, b, b, a]`: - The first element `REF1000 MyGraphCodableApp.AClass` is a reference (`REF`) type. GraphCodable assigns a unique numeric `id` in `REF` to each object it encounters. This object in turn contains a structure corresponding to the key 'astruct' and an object corresponding to the key 'aclass' - First, you see the complete definition of the struct, with its array and its dictionary. They contain native types. The list of all GCodable supported types of Swift Standard Library and Foundation is [here](/Docs/GraphCodableTypes.md). - Then you see: `"aclass": `PTR1001?` This is because aclass has been conditionally archived. The encoder assigns it an attempt `PTR?` and waits for it to be stored unconditionally if it happens. - The second, third and fourth element of the array are pointers `PTR1000` to **b**, which has already been stored as `REF_1000`. Therefore the encoder only stores the `PTR`, which this time is certain (`PTR` without question mark). - The fifth element `REF1001` is `a` which was only conditionally archived previously (`PTR1001?`) and is therefore archived now. *As an exercise*: see what happens when you delete `a` from the array. *As an exercise*: try to see what happens by encoding `"aclass"` *not-conditionally*. ## Binary like output of the encoded data GraphCodable employs some tricks to reduce the size of the data to be stored. For example, types and keys are saved only once in special tables and are addressed to them through a unique ID. By using the '.binaryLike' option you can see the data saved in a format that more closely resembles the binary format actually used. By using the '.showMangledClassNames' option it is possible to see the archived string which will be used to construct the type during dearchiving. Graphcodable tries `_typeByName( MangledName )` first and then `NSClassFromString( NSTypeName )` Try replacing: `print( try GraphEncoder().dump( inRoot ) )` with: `print( try GraphEncoder().dump( inRoot, options: [.binaryLike,.showMangledClassNames] ) )` in the previous code. The result: ``` == HEADER ======================================================== FILETYPE = gcodable V0, U0 = "", U1 = 0, U2 = 0 == TYPEMAP ======================================================= TYPE100: MyGraphCodableApp.AClass V0 MangledName = 17MyGraphCodableApp6AClassC NSTypeName = MyGraphCodableApp.AClass == BODY ========================================================== - VAL - REF1000 TYPE100 + KEY100: VAL + KEY101: [1, 2, 3] + KEY102: ["5": 5, "4": 4] . + KEY103: PTR1001? . - PTR1000 - PTR1000 - PTR1000 - REF1001 TYPE100 + KEY100: VAL + KEY101: [1, 2, 3] + KEY102: ["4": 4, "5": 5] . + KEY103: nil . . == KEYMAP ======================================================== KEY101: "array" KEY103: "aclass" KEY102: "dict" KEY100: "astruct" ================================================================== ``` You can see: - The **HEADER** section, with the file format name (gcodable), its global version, and some unused fields; - The **TYPEMAP** section, in which an **TypeID** is associated with each class with its version; - The **BODY** section, which uses typeIDs and keyIDs; - The **KEYMAP** section, in which an **KeyID** is associated with each key used in keyed coding; ## Full Binary Encoding During archiving, all data is transformed into basic simple types (NativeType protocol, implemented by integer and binary floating points numbers, Bool, Strings, Data) and written in binary format. But GraphCodable is capable of saving directly in binary a whole series of "system" types, among which arrays, dictionaries etc… and by default it employs this optimization. You can disable it during archiving using `GraphEncoder( .onlyNativeTypes )`. Try replacing: `print( try GraphEncoder().dump( inRoot ) )` with: `print( try GraphEncoder( .onlyNativeTypes ).dump( inRoot, options: [.binaryLike,.showMangledClassNames] ) )` in the previous code. The result: ``` == HEADER ======================================================== FILETYPE = gcodable V0, U0 = "", U1 = 0, U2 = 0 == TYPEMAP ======================================================= TYPE100: MyGraphCodableApp.AClass V0 MangledName = 17MyGraphCodableApp6AClassC NSTypeName = MyGraphCodableApp.AClass == BODY ========================================================== - VAL - REF1000 TYPE100 + KEY100: VAL + KEY101: VAL - 1 - 2 - 3 . + KEY102: VAL - "5" - 5 - "4" - 4 . . + KEY103: PTR1001? . - PTR1000 - PTR1000 - PTR1000 - REF1001 TYPE100 + KEY100: VAL + KEY101: VAL - 1 - 2 - 3 . + KEY102: VAL - "4" - 4 - "5" - 5 . . + KEY103: nil . . == KEYMAP ======================================================== KEY100: "astruct" KEY103: "aclass" KEY101: "array" KEY102: "dict" ==================================================================``` ``` You can now see how arrays and dictionaries are first decomposed into each element and then stored.