Hprof code cleanup.

This replaces a bunch of structs+functions with C++ objects
and member functions. It shortens names where unambiguous and
reduces visibility where plausible.

The main two changes are to create Hprof and HprofRecord classes
(from the hprof_record_t and hprof_context_t structs) and moves
functions into the classes. It uses stack allocation for the Hprof
object, which saves a few allocation-failure checks. And it uses
constructors and destructors rather than functions that do those
things.

This change should not alter behavior.

Change-Id: I62ce97fae3c1ba6e2ea89fdfdca915b013072868
diff --git a/src/hprof/hprof.cc b/src/hprof/hprof.cc
index 20fc426..e285307 100644
--- a/src/hprof/hprof.cc
+++ b/src/hprof/hprof.cc
@@ -43,26 +43,6 @@
 
 #define kHeadSuffix "-hptemp"
 
-hprof_context_t* hprofStartup(const char *outputFileName, int fd,
-                              bool directToDdms)
-{
-    hprofStartup_String();
-    hprofStartup_Class();
-
-    hprof_context_t *ctx = (hprof_context_t *)malloc(sizeof(*ctx));
-    if (ctx == NULL) {
-        LOG(ERROR) << "hprof: can't allocate context.";
-        return NULL;
-    }
-
-    /* pass in name or descriptor of the output file */
-    hprofContextInit(ctx, strdup(outputFileName), fd, false, directToDdms);
-
-    CHECK(ctx->memFp != NULL);
-
-    return ctx;
-}
-
 // TODO: use File::WriteFully
 int sysWriteFully(int fd, const void* buf, size_t count, const char* logMsg)
 {
@@ -86,127 +66,108 @@
 /*
  * Finish up the hprof dump.  Returns true on success.
  */
-bool hprofShutdown(hprof_context_t *tailCtx)
+bool Hprof::Finish()
 {
     /* flush the "tail" portion of the output */
-    hprofFlushCurrentRecord(tailCtx);
+    StartNewRecord(HPROF_TAG_HEAP_DUMP_END, HPROF_TIME);
+    FlushCurrentRecord();
 
-    /*
-     * Create a new context struct for the start of the file.  We
-     * heap-allocate it so we can share the "free" function.
-     */
-    hprof_context_t *headCtx = (hprof_context_t *)malloc(sizeof(*headCtx));
-    if (headCtx == NULL) {
-        LOG(ERROR) << "hprof: can't allocate context.";
-        hprofFreeContext(tailCtx);
-        return false;
-    }
-    hprofContextInit(headCtx, strdup(tailCtx->fileName), tailCtx->fd, true,
-        tailCtx->directToDdms);
+    // Create a new Hprof for the start of the file (as opposed to this, which is the tail).
+    Hprof headCtx(file_name_, fd_, true, direct_to_ddms_);
+    headCtx.classes_ = classes_;
+    headCtx.strings_ = strings_;
 
-    LOG(INFO) << StringPrintf("hprof: dumping heap strings to \"%s\".", tailCtx->fileName);
-    hprofDumpStrings(headCtx);
-    hprofDumpClasses(headCtx);
+    LOG(INFO) << StringPrintf("hprof: dumping heap strings to \"%s\".", file_name_);
+    headCtx.DumpStrings();
+    headCtx.DumpClasses();
 
     /* Write a dummy stack trace record so the analysis
      * tools don't freak out.
      */
-    hprofStartNewRecord(headCtx, HPROF_TAG_STACK_TRACE, HPROF_TIME);
-    hprofAddU4ToRecord(&headCtx->curRec, HPROF_NULL_STACK_TRACE);
-    hprofAddU4ToRecord(&headCtx->curRec, HPROF_NULL_THREAD);
-    hprofAddU4ToRecord(&headCtx->curRec, 0);    // no frames
+    headCtx.StartNewRecord(HPROF_TAG_STACK_TRACE, HPROF_TIME);
+    headCtx.current_record_.AddU4(HPROF_NULL_STACK_TRACE);
+    headCtx.current_record_.AddU4(HPROF_NULL_THREAD);
+    headCtx.current_record_.AddU4(0);    // no frames
 
-    hprofFlushCurrentRecord(headCtx);
-
-    hprofShutdown_Class();
-    hprofShutdown_String();
+    headCtx.FlushCurrentRecord();
 
     /* flush to ensure memstream pointer and size are updated */
-    fflush(headCtx->memFp);
-    fflush(tailCtx->memFp);
+    fflush(headCtx.mem_fp_);
+    fflush(mem_fp_);
 
-    if (tailCtx->directToDdms) {
+    if (direct_to_ddms_) {
         /* send the data off to DDMS */
         struct iovec iov[2];
-        iov[0].iov_base = headCtx->fileDataPtr;
-        iov[0].iov_len = headCtx->fileDataSize;
-        iov[1].iov_base = tailCtx->fileDataPtr;
-        iov[1].iov_len = tailCtx->fileDataSize;
+        iov[0].iov_base = headCtx.file_data_ptr_;
+        iov[0].iov_len = headCtx.file_data_size_;
+        iov[1].iov_base = file_data_ptr_;
+        iov[1].iov_len = file_data_size_;
         Dbg::DdmSendChunkV(CHUNK_TYPE("HPDS"), iov, 2);
     } else {
         /*
          * Open the output file, and copy the head and tail to it.
          */
-        CHECK(headCtx->fd == tailCtx->fd);
+        CHECK_EQ(headCtx.fd_, fd_);
 
         int outFd;
-        if (headCtx->fd >= 0) {
-            outFd = dup(headCtx->fd);
+        if (headCtx.fd_ >= 0) {
+            outFd = dup(headCtx.fd_);
             if (outFd < 0) {
-                LOG(ERROR) << StringPrintf("dup(%d) failed: %s", headCtx->fd, strerror(errno));
+                LOG(ERROR) << StringPrintf("dup(%d) failed: %s", headCtx.fd_, strerror(errno));
                 /* continue to fail-handler below */
             }
         } else {
-            outFd = open(tailCtx->fileName, O_WRONLY|O_CREAT|O_TRUNC, 0644);
+            outFd = open(file_name_, O_WRONLY|O_CREAT|O_TRUNC, 0644);
             if (outFd < 0) {
-                LOG(ERROR) << StringPrintf("can't open %s: %s", headCtx->fileName, strerror(errno));
+                LOG(ERROR) << StringPrintf("can't open %s: %s", headCtx.file_name_, strerror(errno));
                 /* continue to fail-handler below */
             }
         }
         if (outFd < 0) {
-            hprofFreeContext(headCtx);
-            hprofFreeContext(tailCtx);
             return false;
         }
 
-        int result;
-        result = sysWriteFully(outFd, headCtx->fileDataPtr,
-            headCtx->fileDataSize, "hprof-head");
-        result |= sysWriteFully(outFd, tailCtx->fileDataPtr,
-            tailCtx->fileDataSize, "hprof-tail");
+        int result = sysWriteFully(outFd, headCtx.file_data_ptr_,
+            headCtx.file_data_size_, "hprof-head");
+        result |= sysWriteFully(outFd, file_data_ptr_, file_data_size_, "hprof-tail");
         close(outFd);
         if (result != 0) {
-            hprofFreeContext(headCtx);
-            hprofFreeContext(tailCtx);
             return false;
         }
     }
 
     /* throw out a log message for the benefit of "runhat" */
     LOG(INFO) << StringPrintf("hprof: heap dump completed (%dKB)",
-        (headCtx->fileDataSize + tailCtx->fileDataSize + 1023) / 1024);
-
-    hprofFreeContext(headCtx);
-    hprofFreeContext(tailCtx);
+        (headCtx.file_data_size_ + file_data_size_ + 1023) / 1024);
 
     return true;
 }
 
-/*
- * Free any heap-allocated items in "ctx", and then free "ctx" itself.
- */
-void hprofFreeContext(hprof_context_t *ctx)
-{
-    CHECK(ctx != NULL);
+Hprof::~Hprof() {
+    /* we don't own ctx->fd_, do not close */
 
-    /* we don't own ctx->fd, do not close */
-
-    if (ctx->memFp != NULL)
-        fclose(ctx->memFp);
-    free(ctx->curRec.body);
-    free(ctx->fileName);
-    free(ctx->fileDataPtr);
-    free(ctx);
+    if (mem_fp_ != NULL) {
+        fclose(mem_fp_);
+    }
+    free(current_record_.body_);
+    free(file_name_);
+    free(file_data_ptr_);
 }
 
 /*
  * Visitor invoked on every root reference.
  */
 void HprofRootVisitor(const Object* obj, void* arg) {
+    CHECK(arg != NULL);
+    Hprof* hprof = (Hprof*)arg;
+    hprof->VisitRoot(obj);
+}
+
+void Hprof::VisitRoot(const Object* obj) {
     uint32_t threadId = 0;  // TODO
     /*RootType */ size_t type = 0; // TODO
 
-    static const hprof_heap_tag_t xlate[] = {
+    static const HprofHeapTag xlate[] = {
         HPROF_ROOT_UNKNOWN,
         HPROF_ROOT_JNI_GLOBAL,
         HPROF_ROOT_JNI_LOCAL,
@@ -223,19 +184,16 @@
         HPROF_ROOT_VM_INTERNAL,
         HPROF_ROOT_JNI_MONITOR,
     };
-    hprof_context_t *ctx;
 
-    CHECK(arg != NULL);
-    CHECK(type < (sizeof(xlate) / sizeof(hprof_heap_tag_t)));
+    CHECK_LT(type, sizeof(xlate) / sizeof(HprofHeapTag));
     if (obj == NULL) {
         return;
     }
-    ctx = (hprof_context_t *)arg;
-    ctx->gcScanState = xlate[type];
-    ctx->gcThreadSerialNumber = threadId;
-    hprofMarkRootObject(ctx, obj, 0);
-    ctx->gcScanState = 0;
-    ctx->gcThreadSerialNumber = 0;
+    gc_scan_state_ = xlate[type];
+    gc_thread_serial_number_ = threadId;
+    MarkRootObject(obj, 0);
+    gc_scan_state_ = 0;
+    gc_thread_serial_number_ = 0;
 }
 
 /*
@@ -246,8 +204,8 @@
 {
     CHECK(obj != NULL);
     CHECK(arg != NULL);
-    hprof_context_t *ctx = (hprof_context_t *)arg;
-    DumpHeapObject(ctx, obj);
+    Hprof *hprof = (Hprof*)arg;
+    hprof->DumpHeapObject(obj);
 }
 
 /*
@@ -255,18 +213,15 @@
  * file.
  *
  * If "fd" is >= 0, the output will be written to that file descriptor.
- * Otherwise, "fileName" is used to create an output file.
+ * Otherwise, "file_name_" is used to create an output file.
  *
- * If "directToDdms" is set, the other arguments are ignored, and data is
+ * If "direct_to_ddms_" is set, the other arguments are ignored, and data is
  * sent directly to DDMS.
  *
  * Returns 0 on success, or an error code on failure.
  */
 int DumpHeap(const char* fileName, int fd, bool directToDdms)
 {
-    hprof_context_t *ctx;
-    int success;
-
     CHECK(fileName != NULL);
     ScopedHeapLock lock;
     ScopedThreadStateChange tsc(Thread::Current(), Thread::kRunnable);
@@ -274,15 +229,11 @@
     ThreadList* thread_list = Runtime::Current()->GetThreadList();
     thread_list->SuspendAll();
 
-    ctx = hprofStartup(fileName, fd, directToDdms);
-    if (ctx == NULL) {
-        return -1;
-    }
-    Runtime::Current()->VisitRoots(HprofRootVisitor, ctx);
-    Heap::GetLiveBits()->Walk(HprofBitmapCallback, ctx);
-    hprofFinishHeapDump(ctx);
+    Hprof hprof(fileName, fd, false, directToDdms);
+    Runtime::Current()->VisitRoots(HprofRootVisitor, &hprof);
+    Heap::GetLiveBits()->Walk(HprofBitmapCallback, &hprof);
 //TODO: write a HEAP_SUMMARY record
-    success = hprofShutdown(ctx) ? 0 : -1;
+    int success = hprof.Finish() ? 0 : -1;
     thread_list->ResumeAll();
     return success;
 }
diff --git a/src/hprof/hprof.h b/src/hprof/hprof.h
index e26e2cf..94852bc 100644
--- a/src/hprof/hprof.h
+++ b/src/hprof/hprof.h
@@ -20,6 +20,8 @@
 #include "globals.h"
 #include "file.h"
 #include "object.h"
+#include "unordered_map.h"
+#include "unordered_set.h"
 
 namespace art {
 
@@ -34,12 +36,16 @@
 #define HPROF_NULL_STACK_TRACE   0
 #define HPROF_NULL_THREAD        0
 
-typedef uint32_t hprof_id;
-typedef hprof_id hprof_string_id;
-typedef hprof_id hprof_object_id;
-typedef hprof_id hprof_class_object_id;
+typedef uint32_t HprofId;
+typedef HprofId HprofStringId;
+typedef HprofId HprofObjectId;
+typedef HprofId HprofClassObjectId;
+typedef std::tr1::unordered_set<Class*, ObjectIdentityHash> ClassSet;
+typedef std::tr1::unordered_set<Class*, ObjectIdentityHash>::iterator ClassSetIterator;
+typedef std::tr1::unordered_map<std::string, size_t> StringMap;
+typedef std::tr1::unordered_map<std::string, size_t>::iterator StringMapIterator;
 
-enum hprof_basic_type {
+enum HprofBasicType {
     hprof_basic_object = 2,
     hprof_basic_boolean = 4,
     hprof_basic_char = 5,
@@ -51,7 +57,7 @@
     hprof_basic_long = 11,
 };
 
-enum hprof_tag_t {
+enum HprofTag {
     HPROF_TAG_STRING = 0x01,
     HPROF_TAG_LOAD_CLASS = 0x02,
     HPROF_TAG_UNLOAD_CLASS = 0x03,
@@ -72,7 +78,7 @@
  * HEAP_DUMP and HEAP_DUMP_SEGMENT
  * records:
  */
-enum hprof_heap_tag_t {
+enum HprofHeapTag {
     /* standard */
     HPROF_ROOT_UNKNOWN = 0xFF,
     HPROF_ROOT_JNI_GLOBAL = 0x01,
@@ -104,18 +110,32 @@
  * format is:
  *
  *     uint8_t     TAG: denoting the type of the record
- *     uint32_t     TIME: number of microseconds since the time stamp in the header
- *     uint32_t     LENGTH: number of bytes that follow this uint32_t field
+ *     uint32_t    TIME: number of microseconds since the time stamp in the header
+ *     uint32_t    LENGTH: number of bytes that follow this uint32_t field
  *                    and belong to this record
  *     [uint8_t]*  BODY: as many bytes as specified in the above uint32_t field
  */
-struct hprof_record_t {
-    unsigned char *body;
-    uint32_t time;
-    uint32_t length;
-    size_t allocLen;
-    uint8_t tag;
-    bool dirty;
+class HprofRecord {
+  public:
+    int Flush(FILE *fp);
+    int AddU1(uint8_t value);
+    int AddU2(uint16_t value);
+    int AddU4(uint32_t value);
+    int AddU8(uint64_t value);
+    int AddId(HprofObjectId value);
+    int AddU1List(const uint8_t *values, size_t numValues);
+    int AddU2List(const uint16_t *values, size_t numValues);
+    int AddU4List(const uint32_t *values, size_t numValues);
+    int AddU8List(const uint64_t *values, size_t numValues);
+    int AddIdList(const HprofObjectId *values, size_t numValues);
+    int AddUtf8String(const char *str);
+
+    unsigned char *body_;
+    uint32_t time_;
+    uint32_t length_;
+    size_t alloc_length_;
+    uint8_t tag_;
+    bool dirty_;
 };
 
 enum HprofHeapId {
@@ -124,90 +144,57 @@
     HPROF_HEAP_APP = 'A'
 };
 
-struct hprof_context_t {
-    /* curRec *must* be first so that we
+class Hprof {
+  public:
+    Hprof(const char *outputFileName, int fd, bool writeHeader, bool directToDdms);
+    ~Hprof();
+
+    void VisitRoot(const Object* obj);
+    int DumpHeapObject(const Object *obj);
+    bool Finish();
+
+  private:
+    int DumpClasses();
+    int DumpStrings();
+    int StartNewRecord(uint8_t tag, uint32_t time);
+    int FlushCurrentRecord();
+    int MarkRootObject(const Object *obj, jobject jniObj);
+    HprofClassObjectId LookupClassId(Class* clazz);
+    HprofStringId LookupStringId(String* string);
+    HprofStringId LookupStringId(const char* string);
+    HprofStringId LookupStringId(std::string string);
+    HprofStringId LookupClassNameId(Class* clazz);
+
+    /* current_record_ *must* be first so that we
      * can cast from a context to a record.
      */
-    hprof_record_t curRec;
+    HprofRecord current_record_;
 
-    uint32_t gcThreadSerialNumber;
-    uint8_t gcScanState;
-    HprofHeapId currentHeap;    // which heap we're currently emitting
-    uint32_t stackTraceSerialNumber;
-    size_t objectsInSegment;
+    uint32_t gc_thread_serial_number_;
+    uint8_t gc_scan_state_;
+    HprofHeapId current_heap_;    // which heap we're currently emitting
+    size_t objects_in_segment_;
 
     /*
-     * If directToDdms is set, "fileName" and "fd" will be ignored.
-     * Otherwise, "fileName" must be valid, though if "fd" >= 0 it will
+     * If direct_to_ddms_ is set, "file_name_" and "fd" will be ignored.
+     * Otherwise, "file_name_" must be valid, though if "fd" >= 0 it will
      * only be used for debug messages.
      */
-    bool directToDdms;
-    char *fileName;
-    char *fileDataPtr;          // for open_memstream
-    size_t fileDataSize;        // for open_memstream
-    FILE *memFp;
-    int fd;
+    bool direct_to_ddms_;
+    char *file_name_;
+    char *file_data_ptr_;          // for open_memstream
+    size_t file_data_size_;        // for open_memstream
+    FILE *mem_fp_;
+    int fd_;
+
+    mutable Mutex classes_lock_;
+    ClassSet classes_;
+
+    size_t next_string_id_;
+    mutable Mutex strings_lock_;
+    StringMap strings_;
 };
 
-hprof_string_id hprofLookupStringId(String* string);
-hprof_string_id hprofLookupStringId(const char* string);
-hprof_string_id hprofLookupStringId(std::string string);
-
-int hprofDumpStrings(hprof_context_t *ctx);
-
-int hprofStartup_String(void);
-int hprofShutdown_String(void);
-
-hprof_class_object_id hprofLookupClassId(Class* clazz);
-
-int hprofDumpClasses(hprof_context_t *ctx);
-
-int hprofStartup_Class(void);
-int hprofShutdown_Class(void);
-
-int hprofStartHeapDump(hprof_context_t *ctx);
-int hprofFinishHeapDump(hprof_context_t *ctx);
-
-int hprofSetGcScanState(hprof_context_t *ctx,
-                        hprof_heap_tag_t state, uint32_t threadSerialNumber);
-int hprofMarkRootObject(hprof_context_t *ctx,
-                        const Object *obj, jobject jniObj);
-
-int DumpHeapObject(hprof_context_t *ctx, const Object *obj);
-
-void hprofContextInit(hprof_context_t *ctx, char *fileName, int fd,
-                      bool writeHeader, bool directToDdms);
-
-int hprofFlushRecord(hprof_record_t *rec, FILE *fp);
-int hprofFlushCurrentRecord(hprof_context_t *ctx);
-int hprofStartNewRecord(hprof_context_t *ctx, uint8_t tag, uint32_t time);
-
-int hprofAddU1ToRecord(hprof_record_t *rec, uint8_t value);
-int hprofAddU1ListToRecord(hprof_record_t *rec,
-                           const uint8_t *values, size_t numValues);
-
-int hprofAddUtf8StringToRecord(hprof_record_t *rec, const char *str);
-
-int hprofAddU2ToRecord(hprof_record_t *rec, uint16_t value);
-int hprofAddU2ListToRecord(hprof_record_t *rec,
-                           const uint16_t *values, size_t numValues);
-
-int hprofAddU4ToRecord(hprof_record_t *rec, uint32_t value);
-int hprofAddU4ListToRecord(hprof_record_t *rec,
-                           const uint32_t *values, size_t numValues);
-
-int hprofAddU8ToRecord(hprof_record_t *rec, uint64_t value);
-int hprofAddU8ListToRecord(hprof_record_t *rec,
-                           const uint64_t *values, size_t numValues);
-
-#define hprofAddIdToRecord(rec, id) hprofAddU4ToRecord((rec), (uint32_t)(id))
-#define hprofAddIdListToRecord(rec, values, numValues) \
-            hprofAddU4ListToRecord((rec), (const uint32_t *)(values), (numValues))
-
-hprof_context_t* hprofStartup(const char *outputFileName, int fd,
-    bool directToDdms);
-bool hprofShutdown(hprof_context_t *ctx);
-void hprofFreeContext(hprof_context_t *ctx);
 int DumpHeap(const char* fileName, int fd, bool directToDdms);
 
 }  // namespace hprof
diff --git a/src/hprof/hprof_class.cc b/src/hprof/hprof_class.cc
index e601e91..ebedc2d 100644
--- a/src/hprof/hprof_class.cc
+++ b/src/hprof/hprof_class.cc
@@ -26,29 +26,16 @@
 
 namespace hprof {
 
-typedef std::tr1::unordered_set<Class*, ObjectIdentityHash> ClassSet;
-typedef std::tr1::unordered_set<Class*, ObjectIdentityHash>::iterator ClassSetIterator;
-static Mutex classes_lock_("hprof classes");
-static ClassSet classes_;
-
-int hprofStartup_Class() {
-    return 0;
+HprofStringId Hprof::LookupClassNameId(Class* clazz) {
+    return LookupStringId(PrettyDescriptor(clazz->GetDescriptor()));
 }
 
-int hprofShutdown_Class() {
-    return 0;
-}
-
-static int getPrettyClassNameId(Class* clazz) {
-    return hprofLookupStringId(PrettyDescriptor(clazz->GetDescriptor()));
-}
-
-hprof_class_object_id hprofLookupClassId(Class* clazz) {
+HprofClassObjectId Hprof::LookupClassId(Class* clazz) {
     if (clazz == NULL) {
         /* Someone's probably looking up the superclass
          * of java.lang.Object or of a primitive class.
          */
-        return (hprof_class_object_id)0;
+        return (HprofClassObjectId)0;
     }
 
     MutexLock mu(classes_lock_);
@@ -56,21 +43,17 @@
     std::pair<ClassSetIterator, bool> result = classes_.insert(clazz);
     Class* present = *result.first;
 
-    /* Make sure that the class's name is in the string table.
-     * This is a bunch of extra work that we only have to do
-     * because of the order of tables in the output file
-     * (strings need to be dumped before classes).
-     */
-    getPrettyClassNameId(clazz);
+    // Make sure that we've assigned a string ID for this class' name
+    LookupClassNameId(clazz);
 
-    CHECK(present == clazz);
-    return (hprof_string_id) present;
+    CHECK_EQ(present, clazz);
+    return (HprofStringId) present;
 }
 
-int hprofDumpClasses(hprof_context_t *ctx) {
+int Hprof::DumpClasses() {
     MutexLock mu(classes_lock_);
 
-    hprof_record_t *rec = &ctx->curRec;
+    HprofRecord *rec = &current_record_;
 
     uint32_t nextSerialNumber = 1;
 
@@ -78,7 +61,7 @@
         Class* clazz = *it;
         CHECK(clazz != NULL);
 
-        int err = hprofStartNewRecord(ctx, HPROF_TAG_LOAD_CLASS, HPROF_TIME);
+        int err = StartNewRecord(HPROF_TAG_LOAD_CLASS, HPROF_TIME);
         if (err != 0) {
             return err;
         }
@@ -92,10 +75,10 @@
          *
          * We use the address of the class object structure as its ID.
          */
-        hprofAddU4ToRecord(rec, nextSerialNumber++);
-        hprofAddIdToRecord(rec, (hprof_class_object_id) clazz);
-        hprofAddU4ToRecord(rec, HPROF_NULL_STACK_TRACE);
-        hprofAddIdToRecord(rec, getPrettyClassNameId(clazz));
+        rec->AddU4(nextSerialNumber++);
+        rec->AddId((HprofClassObjectId) clazz);
+        rec->AddU4(HPROF_NULL_STACK_TRACE);
+        rec->AddId(LookupClassNameId(clazz));
     }
 
     return 0;
diff --git a/src/hprof/hprof_heap.cc b/src/hprof/hprof_heap.cc
index 5c23bbb..84168e3 100644
--- a/src/hprof/hprof_heap.cc
+++ b/src/hprof/hprof_heap.cc
@@ -41,36 +41,12 @@
 /* The ID for the synthetic object generated to account for class
  * Static overhead.
  */
-#define CLASS_STATICS_ID(clazz) ((hprof_object_id)(((uint32_t)(clazz)) | 1))
+#define CLASS_STATICS_ID(clazz) ((HprofObjectId)(((uint32_t)(clazz)) | 1))
 
-int hprofStartHeapDump(hprof_context_t *ctx)
-{
-    ctx->objectsInSegment = OBJECTS_PER_SEGMENT;
-    ctx->currentHeap = HPROF_HEAP_DEFAULT;
-    return 0;
-}
-
-int hprofFinishHeapDump(hprof_context_t *ctx)
-{
-    return hprofStartNewRecord(ctx, HPROF_TAG_HEAP_DUMP_END, HPROF_TIME);
-}
-
-int hprofSetGcScanState(hprof_context_t *ctx,
-                        hprof_heap_tag_t state,
-                        uint32_t threadSerialNumber)
-{
-    /* Used by hprofMarkRootObject()
-     */
-    ctx->gcScanState = state;
-    ctx->gcThreadSerialNumber = threadSerialNumber;
-    return 0;
-}
-
-static hprof_basic_type signatureToBasicTypeAndSize(const char *sig,
-                                                    size_t *sizeOut)
+static HprofBasicType signatureToBasicTypeAndSize(const char *sig, size_t *sizeOut)
 {
     char c = sig[0];
-    hprof_basic_type ret;
+    HprofBasicType ret;
     size_t size;
 
     switch (c) {
@@ -94,10 +70,10 @@
     return ret;
 }
 
-static hprof_basic_type primitiveToBasicTypeAndSize(Primitive::Type prim,
+static HprofBasicType primitiveToBasicTypeAndSize(Primitive::Type prim,
                                                     size_t *sizeOut)
 {
-    hprof_basic_type ret;
+    HprofBasicType ret;
     size_t size;
 
     switch (prim) {
@@ -120,32 +96,28 @@
 }
 
 /* Always called when marking objects, but only does
- * something when ctx->gcScanState is non-zero, which is usually
+ * something when ctx->gc_scan_state_ is non-zero, which is usually
  * only true when marking the root set or unreachable
  * objects.  Used to add rootset references to obj.
  */
-int hprofMarkRootObject(hprof_context_t *ctx, const Object *obj, jobject jniObj)
+int Hprof::MarkRootObject(const Object *obj, jobject jniObj)
 {
-    hprof_record_t *rec = &ctx->curRec;
-    int err;
-    hprof_heap_tag_t heapTag = (hprof_heap_tag_t)ctx->gcScanState;
+    HprofRecord *rec = &current_record_;
+    int err; // TODO: we may return this uninitialized
+    HprofHeapTag heapTag = (HprofHeapTag)gc_scan_state_;
 
     if (heapTag == 0) {
         return 0;
     }
 
-    if (ctx->objectsInSegment >= OBJECTS_PER_SEGMENT ||
-        rec->length >= BYTES_PER_SEGMENT)
-    {
-        /* This flushes the old segment and starts a new one.
-         */
-        hprofStartNewRecord(ctx, HPROF_TAG_HEAP_DUMP_SEGMENT, HPROF_TIME);
-        ctx->objectsInSegment = 0;
+    if (objects_in_segment_ >= OBJECTS_PER_SEGMENT || rec->length_ >= BYTES_PER_SEGMENT) {
+        // This flushes the old segment and starts a new one.
+        StartNewRecord(HPROF_TAG_HEAP_DUMP_SEGMENT, HPROF_TIME);
+        objects_in_segment_ = 0;
     }
 
     switch (heapTag) {
-    /* ID: object ID
-     */
+    // ID: object ID
     case HPROF_ROOT_UNKNOWN:
     case HPROF_ROOT_STICKY_CLASS:
     case HPROF_ROOT_MONITOR_USED:
@@ -154,17 +126,17 @@
     case HPROF_ROOT_DEBUGGER:
     case HPROF_ROOT_REFERENCE_CLEANUP:
     case HPROF_ROOT_VM_INTERNAL:
-        hprofAddU1ToRecord(rec, heapTag);
-        hprofAddIdToRecord(rec, (hprof_object_id)obj);
+        rec->AddU1(heapTag);
+        rec->AddId((HprofObjectId)obj);
         break;
 
     /* ID: object ID
      * ID: JNI global ref ID
      */
     case HPROF_ROOT_JNI_GLOBAL:
-        hprofAddU1ToRecord(rec, heapTag);
-        hprofAddIdToRecord(rec, (hprof_object_id)obj);
-        hprofAddIdToRecord(rec, (hprof_id)jniObj);
+        rec->AddU1(heapTag);
+        rec->AddId((HprofObjectId)obj);
+        rec->AddId((HprofId)jniObj);
         break;
 
     /* ID: object ID
@@ -174,10 +146,10 @@
     case HPROF_ROOT_JNI_LOCAL:
     case HPROF_ROOT_JNI_MONITOR:
     case HPROF_ROOT_JAVA_FRAME:
-        hprofAddU1ToRecord(rec, heapTag);
-        hprofAddIdToRecord(rec, (hprof_object_id)obj);
-        hprofAddU4ToRecord(rec, ctx->gcThreadSerialNumber);
-        hprofAddU4ToRecord(rec, (uint32_t)-1);
+        rec->AddU1(heapTag);
+        rec->AddId((HprofObjectId)obj);
+        rec->AddU4(gc_thread_serial_number_);
+        rec->AddU4((uint32_t)-1);
         break;
 
     /* ID: object ID
@@ -185,9 +157,9 @@
      */
     case HPROF_ROOT_NATIVE_STACK:
     case HPROF_ROOT_THREAD_BLOCK:
-        hprofAddU1ToRecord(rec, heapTag);
-        hprofAddIdToRecord(rec, (hprof_object_id)obj);
-        hprofAddU4ToRecord(rec, ctx->gcThreadSerialNumber);
+        rec->AddU1(heapTag);
+        rec->AddId((HprofObjectId)obj);
+        rec->AddU4(gc_thread_serial_number_);
         break;
 
     /* ID: thread object ID
@@ -195,10 +167,10 @@
      * uint32_t: stack trace serial number
      */
     case HPROF_ROOT_THREAD_OBJECT:
-        hprofAddU1ToRecord(rec, heapTag);
-        hprofAddIdToRecord(rec, (hprof_object_id)obj);
-        hprofAddU4ToRecord(rec, ctx->gcThreadSerialNumber);
-        hprofAddU4ToRecord(rec, (uint32_t)-1);    //xxx
+        rec->AddU1(heapTag);
+        rec->AddId((HprofObjectId)obj);
+        rec->AddU4(gc_thread_serial_number_);
+        rec->AddU4((uint32_t)-1);    //xxx
         break;
 
     default:
@@ -206,7 +178,7 @@
         break;
     }
 
-    ctx->objectsInSegment++;
+    objects_in_segment_++;
 
     return err;
 }
@@ -216,46 +188,46 @@
     return HPROF_NULL_STACK_TRACE;
 }
 
-int DumpHeapObject(hprof_context_t* ctx, const Object* obj) {
+int Hprof::DumpHeapObject(const Object* obj) {
   Class* clazz;
-  hprof_record_t *rec = &ctx->curRec;
+  HprofRecord *rec = &current_record_;
 
   HprofHeapId desiredHeap = false ? HPROF_HEAP_ZYGOTE : HPROF_HEAP_APP; // TODO: zygote objects?
 
-  if (ctx->objectsInSegment >= OBJECTS_PER_SEGMENT || rec->length >= BYTES_PER_SEGMENT) {
+  if (objects_in_segment_ >= OBJECTS_PER_SEGMENT || rec->length_ >= BYTES_PER_SEGMENT) {
     /* This flushes the old segment and starts a new one.
      */
-    hprofStartNewRecord(ctx, HPROF_TAG_HEAP_DUMP_SEGMENT, HPROF_TIME);
-    ctx->objectsInSegment = 0;
+    StartNewRecord(HPROF_TAG_HEAP_DUMP_SEGMENT, HPROF_TIME);
+    objects_in_segment_ = 0;
 
     /* Starting a new HEAP_DUMP resets the heap to default.
      */
-    ctx->currentHeap = HPROF_HEAP_DEFAULT;
+    current_heap_ = HPROF_HEAP_DEFAULT;
   }
 
-  if (desiredHeap != ctx->currentHeap) {
-    hprof_string_id nameId;
+  if (desiredHeap != current_heap_) {
+    HprofStringId nameId;
 
     /* This object is in a different heap than the current one.
      * Emit a HEAP_DUMP_INFO tag to change heaps.
      */
-    hprofAddU1ToRecord(rec, HPROF_HEAP_DUMP_INFO);
-    hprofAddU4ToRecord(rec, (uint32_t)desiredHeap);   // uint32_t: heap id
+    rec->AddU1(HPROF_HEAP_DUMP_INFO);
+    rec->AddU4((uint32_t)desiredHeap);   // uint32_t: heap id
     switch (desiredHeap) {
     case HPROF_HEAP_APP:
-      nameId = hprofLookupStringId("app");
+      nameId = LookupStringId("app");
       break;
     case HPROF_HEAP_ZYGOTE:
-      nameId = hprofLookupStringId("zygote");
+      nameId = LookupStringId("zygote");
       break;
     default:
       /* Internal error. */
       LOG(ERROR) << "Unexpected desiredHeap";
-      nameId = hprofLookupStringId("<ILLEGAL>");
+      nameId = LookupStringId("<ILLEGAL>");
       break;
     }
-    hprofAddIdToRecord(rec, nameId);
-    ctx->currentHeap = desiredHeap;
+    rec->AddId(nameId);
+    current_heap_ = desiredHeap;
   }
 
   clazz = obj->GetClass();
@@ -277,63 +249,63 @@
         /* Create a byte array to reflect the allocation of the
          * StaticField array at the end of this class.
          */
-        hprofAddU1ToRecord(rec, HPROF_PRIMITIVE_ARRAY_DUMP);
-        hprofAddIdToRecord(rec, CLASS_STATICS_ID(obj));
-        hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
-        hprofAddU4ToRecord(rec, byteLength);
-        hprofAddU1ToRecord(rec, hprof_basic_byte);
+        rec->AddU1(HPROF_PRIMITIVE_ARRAY_DUMP);
+        rec->AddId(CLASS_STATICS_ID(obj));
+        rec->AddU4(stackTraceSerialNumber(obj));
+        rec->AddU4(byteLength);
+        rec->AddU1(hprof_basic_byte);
         for (int i = 0; i < byteLength; i++) {
-          hprofAddU1ToRecord(rec, 0);
+          rec->AddU1(0);
         }
       }
 
-      hprofAddU1ToRecord(rec, HPROF_CLASS_DUMP);
-      hprofAddIdToRecord(rec, hprofLookupClassId(thisClass));
-      hprofAddU4ToRecord(rec, stackTraceSerialNumber(thisClass));
-      hprofAddIdToRecord(rec, hprofLookupClassId(thisClass->GetSuperClass()));
-      hprofAddIdToRecord(rec, (hprof_object_id)thisClass->GetClassLoader());
-      hprofAddIdToRecord(rec, (hprof_object_id)0);    // no signer
-      hprofAddIdToRecord(rec, (hprof_object_id)0);    // no prot domain
-      hprofAddIdToRecord(rec, (hprof_id)0);           // reserved
-      hprofAddIdToRecord(rec, (hprof_id)0);           // reserved
+      rec->AddU1(HPROF_CLASS_DUMP);
+      rec->AddId(LookupClassId(thisClass));
+      rec->AddU4(stackTraceSerialNumber(thisClass));
+      rec->AddId(LookupClassId(thisClass->GetSuperClass()));
+      rec->AddId((HprofObjectId)thisClass->GetClassLoader());
+      rec->AddId((HprofObjectId)0);    // no signer
+      rec->AddId((HprofObjectId)0);    // no prot domain
+      rec->AddId((HprofId)0);           // reserved
+      rec->AddId((HprofId)0);           // reserved
       if (obj->IsClassClass()) {
         // ClassObjects have their static fields appended, so
         // aren't all the same size. But they're at least this
         // size.
-        hprofAddU4ToRecord(rec, sizeof(Class)); // instance size
+        rec->AddU4(sizeof(Class)); // instance size
       } else if (thisClass->IsArrayClass() || thisClass->IsPrimitive()) {
-        hprofAddU4ToRecord(rec, 0);
+        rec->AddU4(0);
       } else {
-        hprofAddU4ToRecord(rec, thisClass->GetObjectSize()); // instance size
+        rec->AddU4(thisClass->GetObjectSize()); // instance size
       }
 
-      hprofAddU2ToRecord(rec, 0);                     // empty const pool
+      rec->AddU2(0);                     // empty const pool
 
       /* Static fields
        */
       if (sFieldCount == 0) {
-        hprofAddU2ToRecord(rec, (uint16_t)0);
+        rec->AddU2((uint16_t)0);
       } else {
-        hprofAddU2ToRecord(rec, (uint16_t)(sFieldCount+1));
-        hprofAddIdToRecord(rec, hprofLookupStringId(STATIC_OVERHEAD_NAME));
-        hprofAddU1ToRecord(rec, hprof_basic_object);
-        hprofAddIdToRecord(rec, CLASS_STATICS_ID(obj));
+        rec->AddU2((uint16_t)(sFieldCount+1));
+        rec->AddId(LookupStringId(STATIC_OVERHEAD_NAME));
+        rec->AddU1(hprof_basic_object);
+        rec->AddId(CLASS_STATICS_ID(obj));
 
         for (size_t i = 0; i < sFieldCount; ++i) {
           Field* f = thisClass->GetStaticField(i);
 
           size_t size;
-          hprof_basic_type t = signatureToBasicTypeAndSize(f->GetTypeDescriptor(), &size);
-          hprofAddIdToRecord(rec, hprofLookupStringId(f->GetName()));
-          hprofAddU1ToRecord(rec, t);
+          HprofBasicType t = signatureToBasicTypeAndSize(f->GetTypeDescriptor(), &size);
+          rec->AddId(LookupStringId(f->GetName()));
+          rec->AddU1(t);
           if (size == 1) {
-            hprofAddU1ToRecord(rec, static_cast<uint8_t>(f->Get32(NULL)));
+            rec->AddU1(static_cast<uint8_t>(f->Get32(NULL)));
           } else if (size == 2) {
-            hprofAddU2ToRecord(rec, static_cast<uint16_t>(f->Get32(NULL)));
+            rec->AddU2(static_cast<uint16_t>(f->Get32(NULL)));
           } else if (size == 4) {
-            hprofAddU4ToRecord(rec, f->Get32(NULL));
+            rec->AddU4(f->Get32(NULL));
           } else if (size == 8) {
-            hprofAddU8ToRecord(rec, f->Get64(NULL));
+            rec->AddU8(f->Get64(NULL));
           } else {
             CHECK(false);
           }
@@ -343,12 +315,12 @@
       /* Instance fields for this class (no superclass fields)
        */
       int iFieldCount = thisClass->IsObjectClass() ? 0 : thisClass->NumInstanceFields();
-      hprofAddU2ToRecord(rec, (uint16_t)iFieldCount);
+      rec->AddU2((uint16_t)iFieldCount);
       for (int i = 0; i < iFieldCount; ++i) {
         Field* f = thisClass->GetInstanceField(i);
-        hprof_basic_type t = signatureToBasicTypeAndSize(f->GetTypeDescriptor(), NULL);
-        hprofAddIdToRecord(rec, hprofLookupStringId(f->GetName()));
-        hprofAddU1ToRecord(rec, t);
+        HprofBasicType t = signatureToBasicTypeAndSize(f->GetTypeDescriptor(), NULL);
+        rec->AddId(LookupStringId(f->GetName()));
+        rec->AddU1(t);
       }
     } else if (clazz->IsArrayClass()) {
       Array *aobj = (Array *)obj;
@@ -357,44 +329,44 @@
       if (obj->IsObjectArray()) {
         /* obj is an object array.
          */
-        hprofAddU1ToRecord(rec, HPROF_OBJECT_ARRAY_DUMP);
+        rec->AddU1(HPROF_OBJECT_ARRAY_DUMP);
 
-        hprofAddIdToRecord(rec, (hprof_object_id)obj);
-        hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
-        hprofAddU4ToRecord(rec, length);
-        hprofAddIdToRecord(rec, hprofLookupClassId(clazz));
+        rec->AddId((HprofObjectId)obj);
+        rec->AddU4(stackTraceSerialNumber(obj));
+        rec->AddU4(length);
+        rec->AddId(LookupClassId(clazz));
 
         /* Dump the elements, which are always objects or NULL.
          */
-        hprofAddIdListToRecord(rec, (const hprof_object_id *)aobj->GetRawData(), length);
+        rec->AddIdList((const HprofObjectId *)aobj->GetRawData(), length);
       } else {
         size_t size;
-        hprof_basic_type t = primitiveToBasicTypeAndSize(clazz->GetComponentType()->GetPrimitiveType(), &size);
+        HprofBasicType t = primitiveToBasicTypeAndSize(clazz->GetComponentType()->GetPrimitiveType(), &size);
 
         /* obj is a primitive array.
          */
 #if DUMP_PRIM_DATA
-        hprofAddU1ToRecord(rec, HPROF_PRIMITIVE_ARRAY_DUMP);
+        rec->AddU1(HPROF_PRIMITIVE_ARRAY_DUMP);
 #else
-        hprofAddU1ToRecord(rec, HPROF_PRIMITIVE_ARRAY_NODATA_DUMP);
+        rec->AddU1(HPROF_PRIMITIVE_ARRAY_NODATA_DUMP);
 #endif
 
-        hprofAddIdToRecord(rec, (hprof_object_id)obj);
-        hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
-        hprofAddU4ToRecord(rec, length);
-        hprofAddU1ToRecord(rec, t);
+        rec->AddId((HprofObjectId)obj);
+        rec->AddU4(stackTraceSerialNumber(obj));
+        rec->AddU4(length);
+        rec->AddU1(t);
 
 #if DUMP_PRIM_DATA
         /* Dump the raw, packed element values.
          */
         if (size == 1) {
-          hprofAddU1ListToRecord(rec, (const uint8_t *)aobj->GetRawData(), length);
+          rec->AddU1List((const uint8_t *)aobj->GetRawData(), length);
         } else if (size == 2) {
-          hprofAddU2ListToRecord(rec, (const uint16_t *)(void *)aobj->GetRawData(), length);
+          rec->AddU2List((const uint16_t *)(void *)aobj->GetRawData(), length);
         } else if (size == 4) {
-          hprofAddU4ListToRecord(rec, (const uint32_t *)(void *)aobj->GetRawData(), length);
+          rec->AddU4List((const uint32_t *)(void *)aobj->GetRawData(), length);
         } else if (size == 8) {
-          hprofAddU8ListToRecord(rec, (const uint64_t *)aobj->GetRawData(), length);
+          rec->AddU8List((const uint64_t *)aobj->GetRawData(), length);
         }
 #endif
       }
@@ -402,17 +374,17 @@
 
       /* obj is an instance object.
        */
-      hprofAddU1ToRecord(rec, HPROF_INSTANCE_DUMP);
-      hprofAddIdToRecord(rec, (hprof_object_id)obj);
-      hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
-      hprofAddIdToRecord(rec, hprofLookupClassId(clazz));
+      rec->AddU1(HPROF_INSTANCE_DUMP);
+      rec->AddId((HprofObjectId)obj);
+      rec->AddU4(stackTraceSerialNumber(obj));
+      rec->AddId(LookupClassId(clazz));
 
       /* Reserve some space for the length of the instance
        * data, which we won't know until we're done writing
        * it.
        */
-      size_t sizePatchOffset = rec->length;
-      hprofAddU4ToRecord(rec, 0x77777777);
+      size_t sizePatchOffset = rec->length_;
+      rec->AddU4(0x77777777);
 
       /* Write the instance data;  fields for this
        * class, followed by super class fields, and so on.
@@ -426,13 +398,13 @@
           size_t size;
           signatureToBasicTypeAndSize(f->GetTypeDescriptor(), &size);
           if (size == 1) {
-            hprofAddU1ToRecord(rec, f->Get32(obj));
+            rec->AddU1(f->Get32(obj));
           } else if (size == 2) {
-            hprofAddU2ToRecord(rec, f->Get32(obj));
+            rec->AddU2(f->Get32(obj));
           } else if (size == 4) {
-            hprofAddU4ToRecord(rec, f->Get32(obj));
+            rec->AddU4(f->Get32(obj));
           } else if (size == 8) {
-            hprofAddU8ToRecord(rec, f->Get64(obj));
+            rec->AddU8(f->Get64(obj));
           } else {
             CHECK(false);
           }
@@ -443,14 +415,14 @@
 
       /* Patch the instance field length.
        */
-      size_t savedLen = rec->length;
-      rec->length = sizePatchOffset;
-      hprofAddU4ToRecord(rec, savedLen - (sizePatchOffset + 4));
-      rec->length = savedLen;
+      size_t savedLen = rec->length_;
+      rec->length_ = sizePatchOffset;
+      rec->AddU4(savedLen - (sizePatchOffset + 4));
+      rec->length_ = savedLen;
     }
   }
 
-  ctx->objectsInSegment++;
+  objects_in_segment_++;
 
   return 0;
 }
diff --git a/src/hprof/hprof_output.cc b/src/hprof/hprof_output.cc
index 5dfdb31..012bfdd 100644
--- a/src/hprof/hprof_output.cc
+++ b/src/hprof/hprof_output.cc
@@ -64,34 +64,42 @@
     } while (0)
 
 /*
- * Initialize an hprof context struct.
- *
- * This will take ownership of "fileName".
+ * Initialize an Hprof.
  */
-void hprofContextInit(hprof_context_t *ctx, char *fileName, int fd,
-                      bool writeHeader, bool directToDdms)
-{
-    memset(ctx, 0, sizeof (*ctx));
-
+Hprof::Hprof(const char *outputFileName, int fd, bool writeHeader, bool directToDdms)
+    : current_record_(),
+      gc_thread_serial_number_(0),
+      gc_scan_state_(0),
+      current_heap_(HPROF_HEAP_DEFAULT),
+      objects_in_segment_(0),
+      direct_to_ddms_(0),
+      file_name_(NULL),
+      file_data_ptr_(NULL),
+      file_data_size_(0),
+      mem_fp_(NULL),
+      fd_(0),
+      classes_lock_("hprof classes"),
+      next_string_id_(0x400000),
+      strings_lock_("hprof strings") {
     /*
      * Have to do this here, because it must happen after we
-     * memset the struct (want to treat fileDataPtr/fileDataSize
+     * memset the struct (want to treat file_data_ptr_/file_data_size_
      * as read-only while the file is open).
      */
-    FILE *fp = open_memstream(&ctx->fileDataPtr, &ctx->fileDataSize);
+    FILE *fp = open_memstream(&file_data_ptr_, &file_data_size_);
     if (fp == NULL) {
         /* not expected */
         LOG(ERROR) << StringPrintf("hprof: open_memstream failed: %s", strerror(errno));
         CHECK(false);
     }
 
-    ctx->directToDdms = directToDdms;
-    ctx->fileName = fileName;
-    ctx->memFp = fp;
-    ctx->fd = fd;
+    direct_to_ddms_ = directToDdms;
+    file_name_ = strdup(outputFileName);
+    mem_fp_ = fp;
+    fd_ = fd;
 
-    ctx->curRec.allocLen = 128;
-    ctx->curRec.body = (unsigned char *)malloc(ctx->curRec.allocLen);
+    current_record_.alloc_length_ = 128;
+    current_record_.body_ = (unsigned char *)malloc(current_record_.alloc_length_);
 //xxx check for/return an error
 
     if (writeHeader) {
@@ -132,195 +140,196 @@
     }
 }
 
-int hprofFlushRecord(hprof_record_t *rec, FILE *fp)
+int HprofRecord::Flush(FILE *fp)
 {
-    if (rec->dirty) {
+    if (dirty_) {
         unsigned char headBuf[sizeof (uint8_t) + 2 * sizeof (uint32_t)];
         int nb;
 
-        headBuf[0] = rec->tag;
-        U4_TO_BUF_BE(headBuf, 1, rec->time);
-        U4_TO_BUF_BE(headBuf, 5, rec->length);
+        headBuf[0] = tag_;
+        U4_TO_BUF_BE(headBuf, 1, time_);
+        U4_TO_BUF_BE(headBuf, 5, length_);
 
         nb = fwrite(headBuf, 1, sizeof(headBuf), fp);
         if (nb != sizeof(headBuf)) {
             return UNIQUE_ERROR();
         }
-        nb = fwrite(rec->body, 1, rec->length, fp);
-        if (nb != (int)rec->length) {
+        nb = fwrite(body_, 1, length_, fp);
+        if (nb != (int)length_) {
             return UNIQUE_ERROR();
         }
 
-        rec->dirty = false;
+        dirty_ = false;
     }
 //xxx if we used less than half (or whatever) of allocLen, shrink the buffer.
 
     return 0;
 }
 
-int hprofFlushCurrentRecord(hprof_context_t *ctx)
+int Hprof::FlushCurrentRecord()
 {
-    return hprofFlushRecord(&ctx->curRec, ctx->memFp);
+    return current_record_.Flush(mem_fp_);
 }
 
-int hprofStartNewRecord(hprof_context_t *ctx, uint8_t tag, uint32_t time)
+int Hprof::StartNewRecord(uint8_t tag, uint32_t time)
 {
-    hprof_record_t *rec = &ctx->curRec;
-    int err;
+    HprofRecord *rec = &current_record_;
 
-    err = hprofFlushRecord(rec, ctx->memFp);
+    int err = rec->Flush(mem_fp_);
     if (err != 0) {
         return err;
-    } else if (rec->dirty) {
+    } else if (rec->dirty_) {
         return UNIQUE_ERROR();
     }
 
-    rec->dirty = true;
-    rec->tag = tag;
-    rec->time = time;
-    rec->length = 0;
+    rec->dirty_ = true;
+    rec->tag_ = tag;
+    rec->time_ = time;
+    rec->length_ = 0;
 
     return 0;
 }
 
-static inline int guaranteeRecordAppend(hprof_record_t *rec, size_t nmore)
+static inline int guaranteeRecordAppend(HprofRecord *rec, size_t nmore)
 {
     size_t minSize;
 
-    minSize = rec->length + nmore;
-    if (minSize > rec->allocLen) {
+    minSize = rec->length_ + nmore;
+    if (minSize > rec->alloc_length_) {
         unsigned char *newBody;
         size_t newAllocLen;
 
-        newAllocLen = rec->allocLen * 2;
+        newAllocLen = rec->alloc_length_ * 2;
         if (newAllocLen < minSize) {
-            newAllocLen = rec->allocLen + nmore + nmore/2;
+            newAllocLen = rec->alloc_length_ + nmore + nmore/2;
         }
-        newBody = (unsigned char *)realloc(rec->body, newAllocLen);
+        newBody = (unsigned char *)realloc(rec->body_, newAllocLen);
         if (newBody != NULL) {
-            rec->body = newBody;
-            rec->allocLen = newAllocLen;
+            rec->body_ = newBody;
+            rec->alloc_length_ = newAllocLen;
         } else {
 //TODO: set an error flag so future ops will fail
             return UNIQUE_ERROR();
         }
     }
 
-    CHECK(rec->length + nmore <= rec->allocLen);
+    CHECK_LE(rec->length_ + nmore, rec->alloc_length_);
     return 0;
 }
 
-int hprofAddU1ListToRecord(hprof_record_t *rec, const uint8_t *values,
-                           size_t numValues)
+int HprofRecord::AddU1List(const uint8_t *values, size_t numValues)
 {
-    int err;
-
-    err = guaranteeRecordAppend(rec, numValues);
+    int err = guaranteeRecordAppend(this, numValues);
     if (err != 0) {
         return err;
     }
 
-    memcpy(rec->body + rec->length, values, numValues);
-    rec->length += numValues;
+    memcpy(body_ + length_, values, numValues);
+    length_ += numValues;
 
     return 0;
 }
 
-int hprofAddU1ToRecord(hprof_record_t *rec, uint8_t value)
+int HprofRecord::AddU1(uint8_t value)
 {
-    int err;
-
-    err = guaranteeRecordAppend(rec, 1);
+    int err = guaranteeRecordAppend(this, 1);
     if (err != 0) {
         return err;
     }
 
-    rec->body[rec->length++] = value;
+    body_[length_++] = value;
 
     return 0;
 }
 
-int hprofAddUtf8StringToRecord(hprof_record_t *rec, const char *str)
+int HprofRecord::AddUtf8String(const char *str)
 {
     /* The terminating NUL character is NOT written.
      */
 //xxx don't do a strlen;  add and grow as necessary, until NUL
-    return hprofAddU1ListToRecord(rec, (const uint8_t *)str, strlen(str));
+    return AddU1List((const uint8_t *)str, strlen(str));
 }
 
-int hprofAddU2ListToRecord(hprof_record_t *rec, const uint16_t *values,
-                           size_t numValues)
+int HprofRecord::AddU2List(const uint16_t *values, size_t numValues)
 {
-    int err = guaranteeRecordAppend(rec, numValues * 2);
+    int err = guaranteeRecordAppend(this, numValues * 2);
     if (err != 0) {
         return err;
     }
 
 //xxx this can be way smarter
 //xxx also, don't do this bytewise if aligned and on a matching-endian arch
-    unsigned char *insert = rec->body + rec->length;
+    unsigned char *insert = body_ + length_;
     for (size_t i = 0; i < numValues; i++) {
         U2_TO_BUF_BE(insert, 0, *values++);
         insert += sizeof(*values);
     }
-    rec->length += numValues * 2;
+    length_ += numValues * 2;
 
     return 0;
 }
 
-int hprofAddU2ToRecord(hprof_record_t *rec, uint16_t value)
+int HprofRecord::AddU2(uint16_t value)
 {
-    return hprofAddU2ListToRecord(rec, &value, 1);
+    return AddU2List(&value, 1);
 }
 
-int hprofAddU4ListToRecord(hprof_record_t *rec, const uint32_t *values,
-                           size_t numValues)
+int HprofRecord::AddIdList(const HprofObjectId *values, size_t numValues)
 {
-    int err = guaranteeRecordAppend(rec, numValues * 4);
+    return AddU4List((const uint32_t*) values, numValues);
+}
+
+int HprofRecord::AddU4List(const uint32_t *values, size_t numValues)
+{
+    int err = guaranteeRecordAppend(this, numValues * 4);
     if (err != 0) {
         return err;
     }
 
 //xxx this can be way smarter
 //xxx also, don't do this bytewise if aligned and on a matching-endian arch
-    unsigned char *insert = rec->body + rec->length;
+    unsigned char *insert = body_ + length_;
     for (size_t i = 0; i < numValues; i++) {
         U4_TO_BUF_BE(insert, 0, *values++);
         insert += sizeof(*values);
     }
-    rec->length += numValues * 4;
+    length_ += numValues * 4;
 
     return 0;
 }
 
-int hprofAddU4ToRecord(hprof_record_t *rec, uint32_t value)
+int HprofRecord::AddU4(uint32_t value)
 {
-    return hprofAddU4ListToRecord(rec, &value, 1);
+    return AddU4List(&value, 1);
 }
 
-int hprofAddU8ListToRecord(hprof_record_t *rec, const uint64_t *values,
-                           size_t numValues)
+int HprofRecord::AddId(HprofObjectId value)
 {
-    int err = guaranteeRecordAppend(rec, numValues * 8);
+    return AddU4((uint32_t) value);
+}
+
+int HprofRecord::AddU8List(const uint64_t *values, size_t numValues)
+{
+    int err = guaranteeRecordAppend(this, numValues * 8);
     if (err != 0) {
         return err;
     }
 
 //xxx this can be way smarter
 //xxx also, don't do this bytewise if aligned and on a matching-endian arch
-    unsigned char *insert = rec->body + rec->length;
+    unsigned char *insert = body_ + length_;
     for (size_t i = 0; i < numValues; i++) {
         U8_TO_BUF_BE(insert, 0, *values++);
         insert += sizeof(*values);
     }
-    rec->length += numValues * 8;
+    length_ += numValues * 8;
 
     return 0;
 }
 
-int hprofAddU8ToRecord(hprof_record_t *rec, uint64_t value)
+int HprofRecord::AddU8(uint64_t value)
 {
-    return hprofAddU8ListToRecord(rec, &value, 1);
+    return AddU8List(&value, 1);
 }
 
 }  // namespace hprof
diff --git a/src/hprof/hprof_string.cc b/src/hprof/hprof_string.cc
index bc1caf2..ab205e7 100644
--- a/src/hprof/hprof_string.cc
+++ b/src/hprof/hprof_string.cc
@@ -25,29 +25,15 @@
 
 namespace hprof {
 
-size_t next_string_id_ = 0x400000;
-typedef std::tr1::unordered_map<std::string, size_t> StringMap;
-typedef std::tr1::unordered_map<std::string, size_t>::iterator StringMapIterator;
-static Mutex strings_lock_("hprof strings");
-static StringMap strings_;
-
-int hprofStartup_String() {
-    return 0;
+HprofStringId Hprof::LookupStringId(String* string) {
+    return LookupStringId(string->ToModifiedUtf8());
 }
 
-int hprofShutdown_String() {
-    return 0;
+HprofStringId Hprof::LookupStringId(const char* string) {
+    return LookupStringId(std::string(string));
 }
 
-hprof_string_id hprofLookupStringId(String* string) {
-    return hprofLookupStringId(string->ToModifiedUtf8());
-}
-
-hprof_string_id hprofLookupStringId(const char* string) {
-    return hprofLookupStringId(std::string(string));
-}
-
-hprof_string_id hprofLookupStringId(std::string string) {
+HprofStringId Hprof::LookupStringId(std::string string) {
     MutexLock mu(strings_lock_);
     if (strings_.find(string) == strings_.end()) {
         strings_[string] = next_string_id_++;
@@ -55,16 +41,16 @@
     return strings_[string];
 }
 
-int hprofDumpStrings(hprof_context_t *ctx) {
+int Hprof::DumpStrings() {
     MutexLock mu(strings_lock_);
 
-    hprof_record_t *rec = &ctx->curRec;
+    HprofRecord *rec = &current_record_;
 
     for (StringMapIterator it = strings_.begin(); it != strings_.end(); ++it) {
         std::string string = (*it).first;
         size_t id = (*it).second;
 
-        int err = hprofStartNewRecord(ctx, HPROF_TAG_STRING, HPROF_TIME);
+        int err = StartNewRecord(HPROF_TAG_STRING, HPROF_TIME);
         if (err != 0) {
             return err;
         }
@@ -77,11 +63,11 @@
          *
          * We use the address of the string data as its ID.
          */
-        err = hprofAddU4ToRecord(rec, id);
+        err = rec->AddU4(id);
         if (err != 0) {
             return err;
         }
-        err = hprofAddUtf8StringToRecord(rec, string.c_str());
+        err = rec->AddUtf8String(string.c_str());
         if (err != 0) {
             return err;
         }