code: package org.gridkit.jvmtool.stacktrace;
  
  import java.io.BufferedInputStream;
  import java.io.DataInputStream;
  import java.io.IOException;
  import java.io.InputStream;
  import java.lang.Thread.State;
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import java.util.NoSuchElementException;
  import java.util.zip.InflaterInputStream;
  
  class StackTraceReaderV1 implements StackTraceReader {
  
      private DataInputStream dis;
      private List<String> stringDic = new ArrayList<String>();
      private List<StackFrame> frameDic = new ArrayList<StackFrame>();
      private Map<StackFrame, StackTraceElement> frameCache = new HashMap<StackFrame, StackTraceElement>();
  
      private boolean loaded;
      private long threadId;
      private long timestamp;
      private StackFrameList trace;
  
      public StackTraceReaderV1(InputStream is) {
          this.dis = new DataInputStream(new BufferedInputStream(new InflaterInputStream(is), 64 << 10));
          stringDic.add(null);
          frameDic.add(null);
          loaded = false;;
      }
  
      @Override
      public boolean isLoaded() {
          return loaded;
      }
  
      @Override
      public long getThreadId() {
          if (!isLoaded()) {
              throw new NoSuchElementException();
          }
          return threadId;
      }
  
      @Override
      public long getTimestamp() {
          if (!isLoaded()) {
              throw new NoSuchElementException();
          }
          return timestamp;
      }
  
      @Override
      public StackTraceElement[] getTrace() {
          if (!isLoaded()) {
              throw new NoSuchElementException();
          }
          StackTraceElement[] strace = new StackTraceElement[trace.depth()];
          for(int i = 0; i != strace.length; ++i) {
              StackFrame frame = trace.frameAt(i);
              StackTraceElement e = frameCache.get(frame);
              if (e == null) {
                  frameCache.put(frame, e = frame.toStackTraceElement());
              }
              strace[i] = e;
          }
          return strace;
      }
  
      @Override
      public StackFrameList getStackTrace() {
          if (!isLoaded()) {
              throw new NoSuchElementException();
          }
          return trace;
      }
  
      @Override
      public String getThreadName() {
          return null;
      }
  
      @Override
      public State getThreadState() {
          return null;
      }
  
      @Override
      public CounterCollection getCounters() {
          return CounterArray.EMPTY;
      }
  
      @Override
      public boolean loadNext() throws IOException {
          loaded = false;
          while(true) {
              int tag = dis.read();
              if (tag < 0) {
                  dis.close();
                  break;
              }
              else if (tag == StackTraceCodec.TAG_STRING) {
                  String str = dis.readUTF();
                  stringDic.add(str);
              }
              else if (tag == StackTraceCodec.TAG_FRAME) {
                  StackFrame ste = readStackTraceElement();
                  frameDic.add(ste);
              }
              else if (tag == StackTraceCodec.TAG_EVENT) {
                  threadId = dis.readLong();
                  timestamp = dis.readLong();
                  int len = StackTraceCodec.readVarInt(dis);
                  StackFrame[] frames = new StackFrame[len];
                  for(int i = 0; i != len; ++i) {
                      int ref = StackTraceCodec.readVarInt(dis);
                      frames[i] = frameDic.get(ref);
                  }
                  trace = new StackFrameArray(frames);
                  loaded = true;
                  break;
              }
              else {
                  throw new IOException("Data format error");
              }
          }
          return loaded;
      }
  
      private StackFrame readStackTraceElement() throws IOException {
          int npkg = StackTraceCodec.readVarInt(dis);
          int ncn = StackTraceCodec.readVarInt(dis);
          int nmtd = StackTraceCodec.readVarInt(dis);
          int nfile = StackTraceCodec.readVarInt(dis);
          int line = StackTraceCodec.readVarInt(dis) - 2;
          String cp = stringDic.get(npkg);
          String cn = stringDic.get(ncn);
          String mtd = stringDic.get(nmtd);
          String file = stringDic.get(nfile);
          StackFrame e = new StackFrame(cp, cn, mtd, file, line);
          return e;
      }
  }
  