code: package org.gridkit.jvmtool.stacktrace;
  
  import java.io.DataOutputStream;
  import java.io.IOException;
  import java.io.OutputStream;
  import java.lang.Thread.State;
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import java.util.zip.DeflaterOutputStream;
  
  class StackTraceWriterV2 implements StackTraceWriter {
  
      private DataOutputStream dos;
      private Map<String, Integer> stringDic = new HashMap<String, Integer>();
      private Map<StackTraceElement, Integer> frameDic = new HashMap<StackTraceElement, Integer>();
      private RotatingStringDictionary dynDic = new RotatingStringDictionary(512);
      private List<String> counterKeys = new ArrayList<String>();
  
      public StackTraceWriterV2(OutputStream os) throws IOException {
          os.write(StackTraceCodec.MAGIC2);
          DeflaterOutputStream def = new DeflaterOutputStream(os);
          this.dos = new DataOutputStream(def);
      }
  
      @Override
      public void write(ThreadSnapshot snap) throws IOException {
          for(StackFrame ste: snap.stackTrace()) {
              intern(ste.toStackTraceElement());
          }
          for(String ckey: snap.counters()) {
              ensureCounter(ckey);
          }
          int threadNameRef = 0;
          if (snap.threadName() != null) {
              threadNameRef = internDyn(snap.threadName());
          }
          dos.writeByte(StackTraceCodec.TAG_EVENT);
          StackTraceCodec.writeVarLong(dos, snap.threadId());
          StackTraceCodec.writeVarInt(dos, threadNameRef);
          StackTraceCodec.writeTimestamp(dos, snap.timestamp());
          writeState(snap.threadState());
          writeCounters(snap.counters());
          writeTrace(snap.stackTrace());
      }
  
      private void writeCounters(CounterCollection counters) throws IOException {
          int n = 0;
          while(n < counterKeys.size()) {
              byte mask = 0;
              for(int i = 0; i < 8; ++i) {
                  if (n + i >= counterKeys.size()) {
                      break;
                  }
                  long val = counters.getValue(counterKeys.get(i));
                  if (val >= 0) {
                      mask |= 1 << i;
                  }
              }
              dos.writeByte(mask);
              n += 8;
          }
          for(String key: counterKeys) {
              long val = counters.getValue(key);
              if (val >= 0) {
                  StackTraceCodec.writeVarLong(dos, val);
              }
          }
      }
  
      private void writeState(State state) throws IOException {
          StackTraceCodec.writeVarInt(dos, state == null ? 0 : (state.ordinal() + 1));
      }
  
      private void writeTrace(StackFrameList trace) throws IOException {
          int n = 0;
          for(@SuppressWarnings("unused") StackFrame sf: trace) {
              ++n;
          }
          StackTraceCodec.writeVarInt(dos, n);
          for(StackFrame ste: trace) {
              StackTraceCodec.writeVarInt(dos, intern(ste.toStackTraceElement()));
          }
      }
  
      private int intern(StackTraceElement ste) throws IOException {
          if (!frameDic.containsKey(ste)) {
              String pkg = ste.getClassName();
              int c = pkg.lastIndexOf('.');
              String cn = c < 0 ? pkg : pkg.substring(c + 1);
              pkg = c < 0 ? null : pkg.substring(0, c);
              String mtd = ste.getMethodName();
              String file = ste.getFileName();
              int line = ste.getLineNumber() + 2;
              if (line < 0) {
                  line = 0;
              }
              int npkg = intern(pkg);
              int ncn = intern(cn);
              int nmtd = intern(mtd);
              int nfile = intern(file);
              dos.writeByte(StackTraceCodec.TAG_FRAME);
              StackTraceCodec.writeVarInt(dos, npkg);
              StackTraceCodec.writeVarInt(dos, ncn);
              StackTraceCodec.writeVarInt(dos, nmtd);
              StackTraceCodec.writeVarInt(dos, nfile);
              StackTraceCodec.writeVarInt(dos, line);
              int n = frameDic.size() + 1;
              frameDic.put(ste, n);
          }
          return frameDic.get(ste);
      }
  
      private int intern(String str) throws IOException {
          if (str == null) {
              return 0;
          }
          if (!stringDic.containsKey(str)) {
              dos.write(StackTraceCodec.TAG_STRING);
              dos.writeUTF(str);
              int n = stringDic.size() + 1;
              stringDic.put(str, n);
          }
          return stringDic.get(str);
      }
  
      private int internDyn(String str) throws IOException {
          if (str == null) {
              return 0;
          }
          int n = dynDic.intern(str);
          if (n < 0) {
              n = ~n;
              dos.write(StackTraceCodec.TAG_DYN_STRING);
              StackTraceCodec.writeVarInt(dos, n + 1);
              dos.writeUTF(str);
          }
          n = n + 1; // leave zero to be null
          return n;
      }
  
      private void ensureCounter(String key) throws IOException {
          int n = counterKeys.indexOf(key);
          if (n < 0) {
              n = counterKeys.size();
              counterKeys.add(key);
              dos.write(StackTraceCodec.TAG_COUNTER);
              dos.writeUTF(key);
          }
      }
  
      @Override
      public void close() {
          try {
              dos.close();
          } catch (IOException e) {
              // ignore
          }
          stringDic.clear();
          frameDic.clear();
      }
  }
  