code: @Override
    public <T extends OutputStream> T serialize(final FSA fsa, T os) throws IOException {
  
      // Prepare space for arc offsets and linearize all the states.
      int[] linearized = linearize(fsa);
  
      /*
       * Calculate the number of bytes required for the node data, if
       * serializing with numbers.
       */
      int nodeDataLength = 0;
      if (withNumbers) {
        this.numbers = FSAUtils.rightLanguageForAllStates(fsa);
        int maxNumber = numbers.get(fsa.getRootNode());
        while (maxNumber > 0) {
          nodeDataLength++;
          maxNumber >>>= 8;
        }
      }
  
      // Calculate minimal goto length.
      int gtl = 1;
      while (true) {
        // First pass: calculate offsets of states.
        if (!emitArcs(fsa, null, linearized, gtl, nodeDataLength)) {
          gtl++;
          continue;
        }
  
        // Second pass: check if goto overflows anywhere.
        if (emitArcs(fsa, null, linearized, gtl, nodeDataLength))
          break;
  
        gtl++;
      }
  
      /*
       * Emit the header.
       */
      FSAHeader.write(os, FSA5.VERSION);
      os.write(fillerByte);
      os.write(annotationByte);
      os.write((nodeDataLength << 4) | gtl);
  
      /*
       * Emit the automaton.
       */
      boolean gtlUnchanged = emitArcs(fsa, os, linearized, gtl, nodeDataLength);
      assert gtlUnchanged : "gtl changed in the final pass.";
  
      return os;
    }
docs: Serialize root state <code>s</code> to an output stream in
  <code>FSA5</code> format.
  
  @see #withNumbers()
  @return Returns <code>os</code> for chaining.