code: def writeRoot(self, root):
          
          output = self.header
          wrapped_root = self.wrapRoot(root)
          self.computeOffsets(wrapped_root, asReference=True, isRoot=True)
          self.trailer = self.trailer._replace(**{'objectRefSize':self.intSize(len(self.computedUniques))})
          self.writeObjectReference(wrapped_root, output)
          output = self.writeObject(wrapped_root, output, setReferencePosition=True)
          
          # output size at this point is an upper bound on how big the
          # object reference offsets need to be.
          self.trailer = self.trailer._replace(**{
              'offsetSize':self.intSize(len(output)),
              'offsetCount':len(self.computedUniques),
              'offsetTableOffset':len(output),
              'topLevelObjectNumber':0
              })
          
          output = self.writeOffsetTable(output)
          output += pack('!xxxxxxBBQQQ', *self.trailer)
          self.file.write(output)
docs: Strategy is:
          - write header
          - wrap root object so everything is hashable
          - compute size of objects which will be written
            - need to do this in order to know how large the object refs
              will be in the list/dict/set reference lists
          - write objects
            - keep objects in writtenReferences
            - keep positions of object references in referencePositions
            - write object references with the length computed previously
          - computer object reference length
          - write object reference positions
          - write trailer