// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.


#include "node.h"
#include "node_buffer.h"

#include "v8.h"
#include "v8-profiler.h"

#include <assert.h>
#include <stdlib.h> // malloc, free
#include <string.h> // memcpy

#ifdef __POSIX__
# include <arpa/inet.h> // htons, htonl
#endif

#define MIN(a,b) ((a) < (b) ? (a) : (b))

#define BUFFER_CLASS_ID (0xBABE)

namespace node {

using namespace v8;

#define SLICE_ARGS(start_arg, end_arg)                               \
  if (!start_arg->IsInt32() || !end_arg->IsInt32()) {                \
    return ThrowException(Exception::TypeError(                      \
          String::New("Bad argument.")));                            \
  }                                                                  \
  int32_t start = start_arg->Int32Value();                           \
  int32_t end = end_arg->Int32Value();                               \
  if (start < 0 || end < 0) {                                        \
    return ThrowException(Exception::TypeError(                      \
          String::New("Bad argument.")));                            \
  }                                                                  \
  if (!(start <= end)) {                                             \
    return ThrowException(Exception::Error(                          \
          String::New("Must have start <= end")));                   \
  }                                                                  \
  if ((size_t)end > parent->length_) {                               \
    return ThrowException(Exception::Error(                          \
          String::New("end cannot be longer than parent.length")));  \
  }


static Persistent<String> length_symbol;
static Persistent<String> chars_written_sym;
static Persistent<String> write_sym;
Persistent<FunctionTemplate> Buffer::constructor_template;


static inline size_t base64_decoded_size(const char *src, size_t size) {
  const char *const end = src + size;
  const int remainder = size % 4;

  size = (size / 4) * 3;
  if (remainder) {
    if (size == 0 && remainder == 1) {
      // special case: 1-byte input cannot be decoded
      size = 0;
    } else {
      // non-padded input, add 1 or 2 extra bytes
      size += 1 + (remainder == 3);
    }
  }

  // check for trailing padding (1 or 2 bytes)
  if (size > 0) {
    if (end[-1] == '=') size--;
    if (end[-2] == '=') size--;
  }

  return size;
}


static size_t ByteLength (Handle<String> string, enum encoding enc) {
  HandleScope scope;

  if (enc == UTF8) {
    return string->Utf8Length();
  } else if (enc == BASE64) {
    String::Utf8Value v(string);
    return base64_decoded_size(*v, v.length());
  } else if (enc == UCS2) {
    return string->Length() * 2;
  } else if (enc == HEX) {
    return string->Length() / 2;
  } else {
    return string->Length();
  }
}


Handle<Object> Buffer::New(Handle<String> string) {
  HandleScope scope;

  // get Buffer from global scope.
  Local<Object> global = v8::Context::GetCurrent()->Global();
  Local<Value> bv = global->Get(String::NewSymbol("Buffer"));
  assert(bv->IsFunction());
  Local<Function> b = Local<Function>::Cast(bv);

  Local<Value> argv[1] = { Local<Value>::New(string) };
  Local<Object> instance = b->NewInstance(1, argv);

  return scope.Close(instance);
}


Buffer* Buffer::New(size_t length) {
  HandleScope scope;

  Local<Value> arg = Integer::NewFromUnsigned(length);
  Local<Object> b = constructor_template->GetFunction()->NewInstance(1, &arg);
  if (b.IsEmpty()) return NULL;

  return ObjectWrap::Unwrap<Buffer>(b);
}


Buffer* Buffer::New(char* data, size_t length) {
  HandleScope scope;

  Local<Value> arg = Integer::NewFromUnsigned(0);
  Local<Object> obj = constructor_template->GetFunction()->NewInstance(1, &arg);

  Buffer *buffer = ObjectWrap::Unwrap<Buffer>(obj);
  buffer->Replace(data, length, NULL, NULL);

  return buffer;
}


Buffer* Buffer::New(char *data, size_t length,
                    free_callback callback, void *hint) {
  HandleScope scope;

  Local<Value> arg = Integer::NewFromUnsigned(0);
  Local<Object> obj = constructor_template->GetFunction()->NewInstance(1, &arg);

  Buffer *buffer = ObjectWrap::Unwrap<Buffer>(obj);
  buffer->Replace(data, length, callback, hint);

  return buffer;
}


Handle<Value> Buffer::New(const Arguments &args) {
  if (!args.IsConstructCall()) {
    return FromConstructorTemplate(constructor_template, args);
  }

  HandleScope scope;

  if (!args[0]->IsUint32()) return ThrowTypeError("Bad argument");

  size_t length = args[0]->Uint32Value();
  if (length > Buffer::kMaxLength) {
    return ThrowRangeError("length > kMaxLength");
  }
  new Buffer(args.This(), length);

  return args.This();
}


Buffer::Buffer(Handle<Object> wrapper, size_t length) : ObjectWrap() {
  Wrap(wrapper);

  length_ = 0;
  callback_ = NULL;
  handle_.SetWrapperClassId(BUFFER_CLASS_ID);

  Replace(NULL, length, NULL, NULL);
}


Buffer::~Buffer() {
  Replace(NULL, 0, NULL, NULL);
}


void Buffer::Replace(char *data, size_t length,
                     free_callback callback, void *hint) {
  HandleScope scope;

  if (callback_) {
    callback_(data_, callback_hint_);
  } else if (length_) {
    delete [] data_;
    V8::AdjustAmountOfExternalAllocatedMemory(
        -static_cast<intptr_t>(sizeof(Buffer) + length_));
  }

  length_ = length;
  callback_ = callback;
  callback_hint_ = hint;

  if (callback_) {
    data_ = data;
  } else if (length_) {
    data_ = new char[length_];
    if (data)
      memcpy(data_, data, length_);
    V8::AdjustAmountOfExternalAllocatedMemory(sizeof(Buffer) + length_);
  } else {
    data_ = NULL;
  }

  handle_->SetIndexedPropertiesToExternalArrayData(data_,
                                                   kExternalUnsignedByteArray,
                                                   length_);
  handle_->Set(length_symbol, Integer::NewFromUnsigned(length_));
}


Handle<Value> Buffer::BinarySlice(const Arguments &args) {
  HandleScope scope;
  Buffer *parent = ObjectWrap::Unwrap<Buffer>(args.This());
  SLICE_ARGS(args[0], args[1])

  char *data = parent->data_ + start;
  //Local<String> string = String::New(data, end - start);

  Local<Value> b =  Encode(data, end - start, BINARY);

  return scope.Close(b);
}


Handle<Value> Buffer::AsciiSlice(const Arguments &args) {
  HandleScope scope;
  Buffer *parent = ObjectWrap::Unwrap<Buffer>(args.This());
  SLICE_ARGS(args[0], args[1])

  char* data = parent->data_ + start;
  Local<String> string = String::New(data, end - start);

  return scope.Close(string);
}


Handle<Value> Buffer::Utf8Slice(const Arguments &args) {
  HandleScope scope;
  Buffer *parent = ObjectWrap::Unwrap<Buffer>(args.This());
  SLICE_ARGS(args[0], args[1])
  char *data = parent->data_ + start;
  Local<String> string = String::New(data, end - start);
  return scope.Close(string);
}

Handle<Value> Buffer::Ucs2Slice(const Arguments &args) {
  HandleScope scope;
  Buffer *parent = ObjectWrap::Unwrap<Buffer>(args.This());
  SLICE_ARGS(args[0], args[1])
  uint16_t *data = (uint16_t*)(parent->data_ + start);
  Local<String> string = String::New(data, (end - start) / 2);
  return scope.Close(string);
}

static const char *base64_table = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                                  "abcdefghijklmnopqrstuvwxyz"
                                  "0123456789+/";

// supports regular and URL-safe base64
static const int unbase64_table[] =
  {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2,-1,-1,-2,-1,-1
  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
  ,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,62,-1,63
  ,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1
  ,-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14
  ,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,63
  ,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40
  ,41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1
  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
  };
#define unbase64(x) unbase64_table[(uint8_t)(x)]


Handle<Value> Buffer::Base64Slice(const Arguments &args) {
  HandleScope scope;
  Buffer *parent = ObjectWrap::Unwrap<Buffer>(args.This());
  SLICE_ARGS(args[0], args[1])

  int n = end - start;
  int out_len = (n + 2 - ((n + 2) % 3)) / 3 * 4;
  char *out = new char[out_len];

  uint8_t bitbuf[3];
  int i = start; // data() index
  int j = 0; // out index
  char c;
  bool b1_oob, b2_oob;

  while (i < end) {
    bitbuf[0] = parent->data_[i++];

    if (i < end) {
      bitbuf[1] = parent->data_[i];
      b1_oob = false;
    }  else {
      bitbuf[1] = 0;
      b1_oob = true;
    }
    i++;

    if (i < end) {
      bitbuf[2] = parent->data_[i];
      b2_oob = false;
    }  else {
      bitbuf[2] = 0;
      b2_oob = true;
    }
    i++;


    c = bitbuf[0] >> 2;
    assert(c < 64);
    out[j++] = base64_table[(int)c];
    assert(j < out_len);

    c = ((bitbuf[0] & 0x03) << 4) | (bitbuf[1] >> 4);
    assert(c < 64);
    out[j++] = base64_table[(int)c];
    assert(j < out_len);

    if (b1_oob) {
      out[j++] = '=';
    } else {
      c = ((bitbuf[1] & 0x0F) << 2) | (bitbuf[2] >> 6);
      assert(c < 64);
      out[j++] = base64_table[(int)c];
    }
    assert(j < out_len);

    if (b2_oob) {
      out[j++] = '=';
    } else {
      c = bitbuf[2] & 0x3F;
      assert(c < 64);
      out[j++]  = base64_table[(int)c];
    }
    assert(j <= out_len);
  }

  Local<String> string = String::New(out, out_len);
  delete [] out;
  return scope.Close(string);
}


// buffer.fill(value, start, end);
Handle<Value> Buffer::Fill(const Arguments &args) {
  HandleScope scope;

  if (!args[0]->IsInt32()) {
    return ThrowException(Exception::Error(String::New(
            "value is not a number")));
  }
  int value = (char)args[0]->Int32Value();

  Buffer *parent = ObjectWrap::Unwrap<Buffer>(args.This());
  SLICE_ARGS(args[1], args[2])

  memset( (void*)(parent->data_ + start),
          value,
          end - start);

  return Undefined();
}


// var bytesCopied = buffer.copy(target, targetStart, sourceStart, sourceEnd);
Handle<Value> Buffer::Copy(const Arguments &args) {
  HandleScope scope;

  Buffer *source = ObjectWrap::Unwrap<Buffer>(args.This());

  if (!Buffer::HasInstance(args[0])) {
    return ThrowException(Exception::TypeError(String::New(
            "First arg should be a Buffer")));
  }

  Local<Object> target = args[0]->ToObject();
  char* target_data = Buffer::Data(target);
  size_t target_length = Buffer::Length(target);
  size_t target_start = args[1]->Uint32Value();
  size_t source_start = args[2]->Uint32Value();
  size_t source_end = args[3]->IsUint32() ? args[3]->Uint32Value()
                                          : source->length_;

  if (source_end < source_start) {
    return ThrowException(Exception::Error(String::New(
            "sourceEnd < sourceStart")));
  }

  // Copy 0 bytes; we're done
  if (source_end == source_start) {
    return scope.Close(Integer::New(0));
  }

  if (target_start >= target_length) {
    return ThrowException(Exception::Error(String::New(
            "targetStart out of bounds")));
  }

  if (source_start >= source->length_) {
    return ThrowException(Exception::Error(String::New(
            "sourceStart out of bounds")));
  }

  if (source_end > source->length_) {
    return ThrowException(Exception::Error(String::New(
            "sourceEnd out of bounds")));
  }

  size_t to_copy = MIN(MIN(source_end - source_start,
                           target_length - target_start),
                           source->length_ - source_start);

  // need to use slightly slower memmove is the ranges might overlap
  memmove((void *)(target_data + target_start),
          (const void*)(source->data_ + source_start),
          to_copy);

  return scope.Close(Integer::New(to_copy));
}


// var charsWritten = buffer.utf8Write(string, offset, [maxLength]);
Handle<Value> Buffer::Utf8Write(const Arguments &args) {
  HandleScope scope;
  Buffer *buffer = ObjectWrap::Unwrap<Buffer>(args.This());

  if (!args[0]->IsString()) {
    return ThrowException(Exception::TypeError(String::New(
            "Argument must be a string")));
  }

  Local<String> s = args[0]->ToString();

  size_t offset = args[1]->Uint32Value();

  int length = s->Length();

  if (length == 0) {
    constructor_template->GetFunction()->Set(chars_written_sym,
                                             Integer::New(0));
    return scope.Close(Integer::New(0));
  }

  if (length > 0 && offset >= buffer->length_) {
    return ThrowException(Exception::TypeError(String::New(
            "Offset is out of bounds")));
  }

  size_t max_length = args[2]->IsUndefined() ? buffer->length_ - offset
                                             : args[2]->Uint32Value();
  max_length = MIN(buffer->length_ - offset, max_length);

  char* p = buffer->data_ + offset;

  int char_written;

  int written = s->WriteUtf8(p,
                             max_length,
                             &char_written,
                             (String::HINT_MANY_WRITES_EXPECTED |
                              String::NO_NULL_TERMINATION));

  constructor_template->GetFunction()->Set(chars_written_sym,
                                           Integer::New(char_written));

  return scope.Close(Integer::New(written));
}


// var charsWritten = buffer.ucs2Write(string, offset, [maxLength]);
Handle<Value> Buffer::Ucs2Write(const Arguments &args) {
  HandleScope scope;
  Buffer *buffer = ObjectWrap::Unwrap<Buffer>(args.This());

  if (!args[0]->IsString()) {
    return ThrowException(Exception::TypeError(String::New(
            "Argument must be a string")));
  }

  Local<String> s = args[0]->ToString();

  size_t offset = args[1]->Uint32Value();

  if (s->Length() > 0 && offset >= buffer->length_) {
    return ThrowException(Exception::TypeError(String::New(
            "Offset is out of bounds")));
  }

  size_t max_length = args[2]->IsUndefined() ? buffer->length_ - offset
                                             : args[2]->Uint32Value();
  max_length = MIN(buffer->length_ - offset, max_length) / 2;

  uint16_t* p = (uint16_t*)(buffer->data_ + offset);

  int written = s->Write(p,
                         0,
                         max_length,
                         (String::HINT_MANY_WRITES_EXPECTED |
                          String::NO_NULL_TERMINATION));

  constructor_template->GetFunction()->Set(chars_written_sym,
                                           Integer::New(written));

  return scope.Close(Integer::New(written * 2));
}


// var charsWritten = buffer.asciiWrite(string, offset);
Handle<Value> Buffer::AsciiWrite(const Arguments &args) {
  HandleScope scope;

  Buffer *buffer = ObjectWrap::Unwrap<Buffer>(args.This());

  if (!args[0]->IsString()) {
    return ThrowException(Exception::TypeError(String::New(
            "Argument must be a string")));
  }

  Local<String> s = args[0]->ToString();
  size_t length = s->Length();
  size_t offset = args[1]->Int32Value();

  if (length > 0 && offset >= buffer->length_) {
    return ThrowException(Exception::TypeError(String::New(
            "Offset is out of bounds")));
  }

  size_t max_length = args[2]->IsUndefined() ? buffer->length_ - offset
                                             : args[2]->Uint32Value();
  max_length = MIN(length, MIN(buffer->length_ - offset, max_length));

  char *p = buffer->data_ + offset;

  int written = s->WriteAscii(p,
                              0,
                              max_length,
                              (String::HINT_MANY_WRITES_EXPECTED |
                               String::NO_NULL_TERMINATION));

  constructor_template->GetFunction()->Set(chars_written_sym,
                                           Integer::New(written));

  return scope.Close(Integer::New(written));
}


// var bytesWritten = buffer.base64Write(string, offset, [maxLength]);
Handle<Value> Buffer::Base64Write(const Arguments &args) {
  HandleScope scope;

  Buffer *buffer = ObjectWrap::Unwrap<Buffer>(args.This());

  if (!args[0]->IsString()) {
    return ThrowException(Exception::TypeError(String::New(
            "Argument must be a string")));
  }

  String::AsciiValue s(args[0]);
  size_t length = s.length();
  size_t offset = args[1]->Int32Value();
  size_t max_length = args[2]->IsUndefined() ? buffer->length_ - offset
                                             : args[2]->Uint32Value();
  max_length = MIN(length, MIN(buffer->length_ - offset, max_length));

  if (max_length && offset >= buffer->length_) {
    return ThrowException(Exception::TypeError(String::New(
            "Offset is out of bounds")));
  }

  char a, b, c, d;
  char* start = buffer->data_ + offset;
  char* dst = start;
  char* const dstEnd = dst + max_length;
  const char* src = *s;
  const char* const srcEnd = src + s.length();

  while (src < srcEnd && dst < dstEnd) {
    int remaining = srcEnd - src;

    while (unbase64(*src) < 0 && src < srcEnd) src++, remaining--;
    if (remaining == 0 || *src == '=') break;
    a = unbase64(*src++);

    while (unbase64(*src) < 0 && src < srcEnd) src++, remaining--;
    if (remaining <= 1 || *src == '=') break;
    b = unbase64(*src++);

    *dst++ = (a << 2) | ((b & 0x30) >> 4);
    if (dst == dstEnd) break;

    while (unbase64(*src) < 0 && src < srcEnd) src++, remaining--;
    if (remaining <= 2 || *src == '=') break;
    c = unbase64(*src++);

    *dst++ = ((b & 0x0F) << 4) | ((c & 0x3C) >> 2);
    if (dst == dstEnd) break;

    while (unbase64(*src) < 0 && src < srcEnd) src++, remaining--;
    if (remaining <= 3 || *src == '=') break;
    d = unbase64(*src++);

    *dst++ = ((c & 0x03) << 6) | (d & 0x3F);
  }

  constructor_template->GetFunction()->Set(chars_written_sym,
                                           Integer::New(dst - start));

  return scope.Close(Integer::New(dst - start));
}


Handle<Value> Buffer::BinaryWrite(const Arguments &args) {
  HandleScope scope;

  Buffer *buffer = ObjectWrap::Unwrap<Buffer>(args.This());

  if (!args[0]->IsString()) {
    return ThrowException(Exception::TypeError(String::New(
            "Argument must be a string")));
  }

  Local<String> s = args[0]->ToString();
  size_t length = s->Length();
  size_t offset = args[1]->Int32Value();

  if (s->Length() > 0 && offset >= buffer->length_) {
    return ThrowException(Exception::TypeError(String::New(
            "Offset is out of bounds")));
  }

  char *p = (char*)buffer->data_ + offset;

  size_t max_length = args[2]->IsUndefined() ? buffer->length_ - offset
                                             : args[2]->Uint32Value();
  max_length = MIN(length, MIN(buffer->length_ - offset, max_length));

  int written = DecodeWrite(p, max_length, s, BINARY);

  constructor_template->GetFunction()->Set(chars_written_sym,
                                           Integer::New(written));

  return scope.Close(Integer::New(written));
}


// var nbytes = Buffer.byteLength("string", "utf8")
Handle<Value> Buffer::ByteLength(const Arguments &args) {
  HandleScope scope;

  if (!args[0]->IsString()) {
    return ThrowException(Exception::TypeError(String::New(
            "Argument must be a string")));
  }

  Local<String> s = args[0]->ToString();
  enum encoding e = ParseEncoding(args[1], UTF8);

  return scope.Close(Integer::New(node::ByteLength(s, e)));
}


Handle<Value> Buffer::MakeFastBuffer(const Arguments &args) {
  HandleScope scope;

  if (!Buffer::HasInstance(args[0])) {
    return ThrowException(Exception::TypeError(String::New(
            "First argument must be a Buffer")));
  }

  Buffer *buffer = ObjectWrap::Unwrap<Buffer>(args[0]->ToObject());
  Local<Object> fast_buffer = args[1]->ToObject();;
  uint32_t offset = args[2]->Uint32Value();
  uint32_t length = args[3]->Uint32Value();

  fast_buffer->SetIndexedPropertiesToExternalArrayData(buffer->data_ + offset,
                                                       kExternalUnsignedByteArray,
                                                       length);

  return Undefined();
}


bool Buffer::HasInstance(v8::Handle<v8::Value> val) {
  if (!val->IsObject()) return false;
  v8::Local<v8::Object> obj = val->ToObject();

  if (obj->GetIndexedPropertiesExternalArrayDataType() == kExternalUnsignedByteArray)
    return true;

  // Also check for SlowBuffers that are empty.
  if (constructor_template->HasInstance(obj))
    return true;

  return false;
}


class RetainedBufferInfo: public v8::RetainedObjectInfo {
public:
  RetainedBufferInfo(Buffer* buffer);
  virtual void Dispose();
  virtual bool IsEquivalent(RetainedObjectInfo* other);
  virtual intptr_t GetHash();
  virtual const char* GetLabel();
  virtual intptr_t GetSizeInBytes();
private:
  Buffer* buffer_;
  static const char label[];
};

const char RetainedBufferInfo::label[] = "Buffer";


RetainedBufferInfo::RetainedBufferInfo(Buffer* buffer): buffer_(buffer) {
}


void RetainedBufferInfo::Dispose() {
  buffer_ = NULL;
  delete this;
}


bool RetainedBufferInfo::IsEquivalent(RetainedObjectInfo* other) {
  return label == other->GetLabel() &&
         buffer_ == static_cast<RetainedBufferInfo*>(other)->buffer_;
}


intptr_t RetainedBufferInfo::GetHash() {
  return reinterpret_cast<intptr_t>(buffer_);
}


const char* RetainedBufferInfo::GetLabel() {
  return label;
}


intptr_t RetainedBufferInfo::GetSizeInBytes() {
  return Buffer::Length(buffer_);
}


RetainedObjectInfo* WrapperInfo(uint16_t class_id, Handle<Value> wrapper) {
  assert(class_id == BUFFER_CLASS_ID);
  assert(Buffer::HasInstance(wrapper));
  Buffer* buffer = Buffer::Unwrap<Buffer>(wrapper.As<Object>());
  return new RetainedBufferInfo(buffer);
}


void Buffer::Initialize(Handle<Object> target) {
  HandleScope scope;

  // sanity checks
  assert(unbase64('/') == 63);
  assert(unbase64('+') == 62);
  assert(unbase64('T') == 19);
  assert(unbase64('Z') == 25);
  assert(unbase64('t') == 45);
  assert(unbase64('z') == 51);
  assert(unbase64(' ') == -2);
  assert(unbase64('\n') == -2);
  assert(unbase64('\r') == -2);

  length_symbol = NODE_PSYMBOL("length");
  chars_written_sym = NODE_PSYMBOL("_charsWritten");

  Local<FunctionTemplate> t = FunctionTemplate::New(Buffer::New);
  constructor_template = Persistent<FunctionTemplate>::New(t);
  constructor_template->InstanceTemplate()->SetInternalFieldCount(1);
  constructor_template->SetClassName(String::NewSymbol("SlowBuffer"));

  // copy free
  NODE_SET_PROTOTYPE_METHOD(constructor_template, "binarySlice", Buffer::BinarySlice);
  NODE_SET_PROTOTYPE_METHOD(constructor_template, "asciiSlice", Buffer::AsciiSlice);
  NODE_SET_PROTOTYPE_METHOD(constructor_template, "base64Slice", Buffer::Base64Slice);
  NODE_SET_PROTOTYPE_METHOD(constructor_template, "ucs2Slice", Buffer::Ucs2Slice);
  // TODO NODE_SET_PROTOTYPE_METHOD(t, "utf16Slice", Utf16Slice);
  // copy
  NODE_SET_PROTOTYPE_METHOD(constructor_template, "utf8Slice", Buffer::Utf8Slice);

  NODE_SET_PROTOTYPE_METHOD(constructor_template, "utf8Write", Buffer::Utf8Write);
  NODE_SET_PROTOTYPE_METHOD(constructor_template, "asciiWrite", Buffer::AsciiWrite);
  NODE_SET_PROTOTYPE_METHOD(constructor_template, "binaryWrite", Buffer::BinaryWrite);
  NODE_SET_PROTOTYPE_METHOD(constructor_template, "base64Write", Buffer::Base64Write);
  NODE_SET_PROTOTYPE_METHOD(constructor_template, "ucs2Write", Buffer::Ucs2Write);
  NODE_SET_PROTOTYPE_METHOD(constructor_template, "fill", Buffer::Fill);
  NODE_SET_PROTOTYPE_METHOD(constructor_template, "copy", Buffer::Copy);

  NODE_SET_METHOD(constructor_template->GetFunction(),
                  "byteLength",
                  Buffer::ByteLength);
  NODE_SET_METHOD(constructor_template->GetFunction(),
                  "makeFastBuffer",
                  Buffer::MakeFastBuffer);

  target->Set(String::NewSymbol("SlowBuffer"), constructor_template->GetFunction());

  HeapProfiler::DefineWrapperClass(BUFFER_CLASS_ID, WrapperInfo);
}


}  // namespace node

NODE_MODULE(node_buffer, node::Buffer::Initialize)
//
//          Copyright Marshall Clow 2009-2010
// Distributed under the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE_1_0.txt or copy at
//          http://www.boost.org/LICENSE_1_0.txt)
//
//

#ifndef _BOOST_MIME_HPP
#define _BOOST_MIME_HPP

#include <list>
#include <string>
#include <vector>
#include <iosfwd>

#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/std_pair.hpp>
#include <boost/spirit/include/phoenix.hpp>  // pulls in all of Phoenix
#include <boost/spirit/include/support_istream_iterator.hpp>
#include <boost/fusion/adapted/struct.hpp>

#include <boost/shared_ptr.hpp>
#include <boost/format.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string.hpp>


// #define  DUMP_MIME_DATA  1


namespace boost { namespace mime {

//  Errors are reported using this exception class
class mime_parsing_error : public std::runtime_error {
public:
    explicit mime_parsing_error ( const std::string & msg ) : std::runtime_error ( msg ) {}
  };

template <class traits> class basic_mime;

namespace detail {

  static const char *k_crlf         = "\015\012";
  static const char *k_package_name     = "Proposed.Boost.Mime";
  static const char *k_package_version    = "0.1";
  static const char *k_content_type_header  = "Content-Type";
  static const char *k_mime_version_header  = "Mime-Version";

  struct default_types {
    typedef std::string string_type;
  //  typedef std::pair < std::string, string_type > header_type;
    typedef std::vector<char> body_type;
    };


  template<typename string_type>
  struct find_mime_header {
    find_mime_header ( const char *str ) : searchFor ( str ) {}
    bool operator () ( const std::pair<std::string, string_type> &val ) const { return boost::iequals ( val.first, searchFor ); }
  private:
    const char *searchFor;
    };


#ifdef  DUMP_MIME_DATA
  struct tracer {
    tracer ( const char *fn ) : fn_ (fn) { std::cout << "->" << fn_ << std::endl; }
    ~tracer () { std::cout << "<-" << fn_ << std::endl; }
    const char *fn_;
    };
#else
  struct tracer {
    tracer ( const char * ) {}
    ~tracer () {}
    };
#endif

//  Parsing a Content-Type header
  typedef std::pair<std::string, std::string> phrase_t;
  typedef std::vector < phrase_t > phrase_container_t;
  struct mime_content_type {
    std::string     type;
    std::string     sub_type;
    phrase_container_t  phrases;
    };
  

  namespace qi  = boost::spirit::qi;
  namespace phx = boost::phoenix;
  using boost::spirit::_val;
  using boost::spirit::_1;
  
  template <typename Iterator, typename Container>
  struct mime_header_parser : qi::grammar<Iterator, Container()>
  {
    mime_header_parser() : mime_header_parser::base_type(mime_headerList)
    {
      mime_headerList = *(mime_header) >> crlf;
      mime_header   = token >> qi::lit ( ':' ) >> value >> crlf;
      token     = qi::char_("a-zA-Z") >> *qi::char_("a-zA-Z_0-9\\-");

    //  In Classifieds/000001, a header begins with a CRLF
      value     = ( valuePart [ _val = _1 ] | qi::eps ) >> *(valueCont [ _val += "\015\012" + _1 ]);
      valueCont   = crlf >> contWS [ _val += _1 ] >> valuePart [ _val += _1 ];
      valuePart   = +qi::char_("\t -~");
      
      contWS      = +qi::char_( " \t");
      crlf      = qi::lit ( k_crlf );

    /*  mime_headerList.name("mime-header-list");
      mime_header.name    ("mime-header");
      token.name          ("mime-token");
      valuePart.name      ("mime-value-part");
      value.name          ("mime-value");

      qi::on_error<qi::fail> ( mime_headerList, 
        std::cout
          << phoenix::val("Error! Expecting ") 
          << qi::labels::_4 
          << phoenix::val(" here: \"") 
          << phoenix::construct<std::string>(qi::labels::_3, qi::labels::_2) 
          << phoenix::val("\"")
          << std::endl
        );
    */  
    }
    
    qi::rule<Iterator, Container()> mime_headerList ;
    qi::rule<Iterator, typename Container::value_type()> mime_header;
    qi::rule<Iterator, std::string()> token, value, valueCont, valuePart, contWS;
    qi::rule<Iterator> crlf;
  };
  
  template<typename Container, typename Iterator>
  static Container read_headers ( Iterator &begin, Iterator end ) {
    tracer t ( __func__ );
    Container retVal;

    mime_header_parser<Iterator, Container> mh_parser;
    bool b = qi::parse ( begin, end, mh_parser, retVal );
    if ( !b )
      throw mime_parsing_error ( "Failed to parse headers" );
      
#ifdef  DUMP_MIME_DATA
    std::cout             << "******Headers*******" << std::endl;
    for ( typename Container::const_iterator iter = retVal.begin (); iter != retVal.end (); ++iter ) {
      std::string val = iter->second;
      size_t idx;
      while ( std::string::npos != ( idx = val.find ( k_crlf )))
        val.replace ( idx, std::strlen ( k_crlf ), "\n" );
      std::cout << iter->first << ": " << val << std::endl;
      }
    std::cout << std::endl << "******Headers*******" << std::endl;
#endif

    return retVal;
    }

//  The structure of a Content-Type mime header is taken from RFC 2045
//    http://www.ietf.org/rfc/rfc2045.txt, section 5.1

  template <typename Iterator>
  struct mime_content_type_parser : qi::grammar<Iterator, mime_content_type()>
  {
    mime_content_type_parser() : mime_content_type_parser::base_type(content_type_header)
    {
        content_type_header =  *qi::lit(' ') >> part >> '/' >> sub_part >> *phrase ;
  
        part        =  token | extension_token;
        sub_part      =  token | extension_token;
  
        phrase        =  qi::lit ( ';' ) >> +ws >> attribute >> '=' >> value >> *ws;
        ws          =  qi::char_( " \t") | line_sep | comment;
        line_sep      =  qi::lexeme[ qi::lit ( k_crlf ) ];
        
        attribute     = token.alias();
        value       = token | quoted_string;
        
        token       = +(qi::char_( " -~" ) - qi::char_( " ()<>@,;:\\\"/[]?=" ));
        comment       =  qi::lit ('(') >> +(qi::char_(" -~" ) - ')' ) >> qi::lit(')');
        quoted_string   =  qi::lit ('"') >> +(qi::char_(" -~" ) - '"' ) >> qi::lit('"');
        extension_token   =  qi::char_ ( "Xx" ) >> qi::lit ( '-' ) >> token;
    }
    
    qi::rule<Iterator, mime_content_type()> content_type_header ;
    qi::rule<Iterator, phrase_t()> phrase ;
    qi::rule<Iterator, std::string()> part, sub_part, token, attribute, value, quoted_string, extension_token;
    qi::rule<Iterator> ws, line_sep, comment;
  };
  
  template<typename string_type>
  mime_content_type parse_content_type ( const string_type &theHeader ) {
    tracer t ( __func__ );
    mime_content_type retVal;
    typename string_type::const_iterator first = theHeader.begin ();
    mime_content_type_parser<typename string_type::const_iterator> ct_parser;
    bool b = qi::parse ( first, theHeader.end (), ct_parser, retVal );
    if (!b)
      throw mime_parsing_error ( "Failed to parse the 'Content-Type' header" );
    
    return retVal;
    }

  template<typename string_type>
  static string_type get_ct_value ( const string_type &ctString, const char *key ) {
    tracer t ( __func__ );
    mime_content_type mc = parse_content_type ( ctString );
    for ( phrase_container_t::const_iterator iter = mc.phrases.begin (); iter != mc.phrases.end (); ++iter )
      if ( boost::iequals ( iter->first, key ))
        return iter->second;
    
    throw std::runtime_error ( str ( boost::format ( "Couldn't find Content-Type phrase (%s)" ) % key ));
    }
  

  //  Replace this with a spirit thing later.
  //  we're looking for '; boundary="<somevalue>".*'
  std::string get_boundary ( const std::string &ctString ) {
    tracer t ( __func__ );
    return get_ct_value ( ctString, "boundary" );
    }


  //  Read the body of a multipart 
  //  Return a Container of containers, where the first is the actual body,
  //  and the rest are the sub-parts.
  //  Note that the body of the multipart can be empty.
  //  If this is the case, then the first separator need not have a crlf

  //  if the marker is "abcde", we could have:
  //    Note that the separators are really CRLF--abcdeCRLF and CRLF--abcde--CRLF
  //
  //  multipart body
  //  --abcde
  //    sub part #1
  //  --abcde
  //    sub part #2
  //  --abcde--
  //
  //  ** or **
  //  In this case, the first separator is --abcdeCRLF
  //
  //  --abcde   (no multipart body!)
  //    sub part #1
  //  --abcde
  //    sub part #2
  //  --abcde--
  
  typedef std::vector<char>   sub_part_t;
  typedef std::vector<sub_part_t> sub_parts_t;

  template<typename bodyContainer>
  struct multipart_body_type {
    bool              prolog_is_missing;
    bodyContainer         body_prolog;
    sub_parts_t           sub_parts;
     bodyContainer          body_epilog;  
    };


  //  Parse a mulitpart body.
  //  Either "--boundaryCRLF" -- in which case the body is empty
  //  or    <some sequence of chars> "CRLF--boundaryCRLF" -- in which case we return the sequence
  //  
  //  I am deliberately not checking for a termination separator here
  template <typename Iterator, typename Container>
  struct multipart_body_parser : qi::grammar<Iterator, Container()> {
    multipart_body_parser( const std::string &boundary, bool &isMissing ) : multipart_body_parser::base_type(mimeBody), m_is_missing ( isMissing ) {
      m_is_missing = false;
    //  Thanks to Michael Caisse for the hint to get this working
      mimeBody  %= bareSep [ phx::ref ( m_is_missing ) = true ] | (+(qi::char_ - sep) >> sep ) ;
      bareSep     = qi::lit("--") >> boundary >> crlf;
      sep         = crlf >> bareSep;
      crlf    = qi::lit ( k_crlf );
      }
  
    bool &m_is_missing;
    qi::rule<Iterator, Container()> mimeBody;
    qi::rule<Iterator> bareSep, sep, crlf;
    };
  
  
  //  Break up a multi-part into its' constituent sub parts.
  template <typename Iterator, typename Container>
  struct multipart_part_parser : qi::grammar<Iterator, Container()> {
    multipart_part_parser( const std::string &boundary ) : multipart_part_parser::base_type(mimeParts) {
      mimeParts = (+(qi::char_ - sep) % (sep >> crlf)) > terminator ;
      sep         = crlf >> qi::lit("--") >> boundary ;
      terminator  = sep >> qi::lit("--") >> crlf ;
      crlf    = qi::lit ( k_crlf );
      }
    qi::rule<Iterator, Container()> mimeParts;
    qi::rule<Iterator> sep, terminator, crlf;
    };


  template<typename Iterator, typename bodyContainer>
  static void read_multipart_body ( Iterator &begin, Iterator end, multipart_body_type<bodyContainer> &mp_body, const std::string &separator ) {
    tracer t ( __func__ );
    typedef bodyContainer innerC;
    innerC mpBody;
    multipart_body_parser <Iterator, innerC> mb_parser (separator, mp_body.prolog_is_missing );
    if ( !qi::parse ( begin, end, mb_parser, mp_body.body_prolog ))
      throw mime_parsing_error ("Failed to parse mime body(1)");
    
    multipart_part_parser <Iterator, sub_parts_t> mp_parser ( separator );
    if ( !qi::parse ( begin, end, mp_parser, mp_body.sub_parts ))
      throw mime_parsing_error ( "Failed to parse mime body(2)");
    std::copy ( begin, end, std::back_inserter ( mp_body.body_epilog ));
    
  #ifdef  DUMP_MIME_DATA
      std::cout << std::endl << ">>****Multipart Body*******" << std::endl;
      std::cout << str ( boost::format ( "Body size %d, sub part count = %d, trailer size = %d %s" ) % mp_body.body_prolog.size () % mp_body.sub_parts.size () % mp_body.body_epilog.size () % ( mp_body.prolog_is_missing ? "(missing)" : "" )) << std::endl;
      std::cout << std::endl << "****** Multipart Body Prolog *******" << std::endl;
      std::copy ( mp_body.body_prolog.begin (), mp_body.body_prolog.end(), std::ostream_iterator<char> ( std::cout ));
      std::cout << std::endl << "****** Multipart Body Epilog *******" << std::endl;
      std::copy ( mp_body.body_epilog.begin (), mp_body.body_epilog.end(), std::ostream_iterator<char> ( std::cout ));
      std::cout << std::endl << "<<****Multipart Body*******" << std::endl;
  #endif
    }


  template<typename Container, typename Iterator>
  static Container read_simplepart_body ( Iterator &begin, Iterator end ) {
    tracer t ( __func__ );
    Container retVal;
    std::copy ( begin, end, std::back_inserter(retVal));
    
#ifdef  DUMP_MIME_DATA
    std::cout << std::endl << ">>****SinglePart Body*******" << std::endl;
    std::cout << str ( boost::format ( "Body size %d" ) % retVal.size ()) << std::endl;
    std::copy ( retVal.begin (), retVal.end(), std::ostream_iterator<char> ( std::cout ));
    std::cout << std::endl << "<<****SinglePart Body*******" << std::endl;
#endif
    return retVal;
    }

//  FIXME: Need to break the headers at 80 chars...
  template<typename headerList>
  void write_headers ( std::ostream &out, const headerList &headers ) {
    if ( headers.size () > 0 ) {
      for ( typename headerList::const_iterator iter = headers.begin (); iter != headers.end (); ++iter )
        out << iter->first << ':' << iter->second << detail::k_crlf;
      }
    out << detail::k_crlf;
    }
  
  template <typename bodyContainer>
  void write_body ( std::ostream &out, const bodyContainer &body ) {
    std::copy ( body.begin (), body.end (), std::ostream_iterator<char> ( out ));
    }
  
  inline void write_boundary ( std::ostream &out, std::string boundary, bool isLast, bool leadingCR = true ) {
    if ( leadingCR )
      out << detail::k_crlf;
    out << "--" << boundary;
    if ( isLast )
      out << "--";
    out << detail::k_crlf;
    }


  template<typename Iterator, typename traits>
  static boost::shared_ptr< basic_mime<traits> > parse_mime ( Iterator &begin, Iterator end, const char *default_content_type = "text/plain" );
  }


template <class traits = detail::default_types>
class basic_mime {
public:

  typedef enum { simple_part, multi_part, message_part } part_kind;
//  Types for headers
  typedef typename traits::string_type      string_type;
  typedef std::pair< std::string, string_type>  headerEntry;
  typedef std::list<headerEntry>          headerList;
  typedef typename headerList::iterator          headerIter;
  typedef typename headerList::const_iterator   constHeaderIter;

//  Types for the parts
  typedef boost::shared_ptr<basic_mime>   mimePtr;
  typedef std::vector<mimePtr>        partList;
  typedef typename partList::iterator        partIter;
  typedef typename partList::const_iterator constPartIter;

//  Type for the body
  typedef typename traits::body_type      bodyContainer;
  typedef boost::shared_ptr<bodyContainer>  mimeBody;
  
// -----------------------------------------------------------
//  Constructors, destructor, assignment, and swap
// -----------------------------------------------------------

  basic_mime ( const char *type, const char *subtype )
    : m_body_prolog_is_missing ( false ), m_body ( new bodyContainer ), m_body_epilog ( new bodyContainer ) {
    if ( NULL == type || NULL == subtype || 0 == std::strlen ( type ) || 0 == std::strlen ( subtype ))
      throw std::runtime_error ( "Can't create a mime part w/o a type or subtype" );
    
  //  We start with just two headers, "Content-Type:" and "Mime-Version"
  //  Everything else is optional.
    m_part_kind = part_kind_from_string_pair ( type, subtype );
    std::string ctString = str ( boost::format ( "%s/%s" ) % type % subtype );
    set_header_value ( detail::k_content_type_header, ctString );
    set_header_value ( detail::k_mime_version_header, str ( boost::format ( "1.0 (%s %s)" ) % detail::k_package_name % detail::k_package_version ));
    }
  
  basic_mime ( const headerList &theHeaders, const string_type &default_content_type )
    : m_body_prolog_is_missing ( false ), m_body ( new bodyContainer ), m_body_epilog ( new bodyContainer ),
      m_default_content_type ( default_content_type ) {
    string_type ct = m_default_content_type;
  
    constHeaderIter found = std::find_if ( theHeaders.begin (), theHeaders.end (), 
                  detail::find_mime_header<string_type> ( detail::k_content_type_header ));
    if ( found != theHeaders.end ())
      ct = found->second;
    
    detail::mime_content_type mct = detail::parse_content_type ( ct );
    m_part_kind = part_kind_from_string_pair ( mct.type, mct.sub_type );
    m_headers = theHeaders;
    }
  
  basic_mime ( const basic_mime &rhs )
      : m_part_kind ( rhs.m_part_kind ), m_headers ( rhs.m_headers ), m_body_prolog_is_missing ( rhs.m_body_prolog_is_missing ), 
        m_body ( new bodyContainer ( *rhs.m_body )), m_body_epilog ( new bodyContainer ( *rhs.m_body_epilog )),
    /*  m_subparts ( rhs.m_subparts ), */ m_default_content_type ( rhs.m_default_content_type )
    {
  //  Copy the parts -- not just the shared pointers
    for ( typename partList::const_iterator iter = rhs.subpart_begin (); iter != rhs.subpart_end (); ++iter )
      m_subparts.push_back ( mimePtr ( new basic_mime ( **iter )));
    }

//  Simple, copy constructor-based assignment
//  If this is not efficient enough, then I can optimize it later
  basic_mime & operator = ( const basic_mime &rhs ) {
    basic_mime temp ( rhs );
    this->swap ( temp );
    return *this;
    } 

  void swap ( basic_mime &rhs ) throw () {
    std::swap ( m_part_kind,        rhs.m_part_kind );
    std::swap ( m_headers,          rhs.m_headers );
    std::swap ( m_body_prolog_is_missing, rhs.m_body_prolog_is_missing );
    std::swap ( m_body,           rhs.m_body );
    std::swap ( m_body_epilog,        rhs.m_body_epilog );
    std::swap ( m_subparts,         rhs.m_subparts );
    std::swap ( m_default_content_type,   rhs.m_default_content_type );
    }

  ~basic_mime () {}
  

//  What kind of part is this (simple, multi, message)
  part_kind get_part_kind () const { return m_part_kind; }

//  Sub-part information
//  FIXME: Need some error checking here
//    No sub-parts for simple parts, for example.
  size_t part_count () const { return m_subparts.size (); }

  boost::shared_ptr <basic_mime> operator [] ( std::size_t idx ) const {
    check_subpart_index ( idx );
    return m_subparts [ idx ];
    }

  void append_part ( boost::shared_ptr<basic_mime> newPart ) {
    check_subpart_append ();
    m_subparts.push_back ( newPart );
    }
  
  partIter    subpart_begin ()    { return m_subparts.begin (); }
  partIter    subpart_end   ()    { return m_subparts.end   (); }
  constPartIter subpart_begin () const  { return m_subparts.begin (); }
  constPartIter subpart_end   () const  { return m_subparts.end   (); }
  
//  Reading the raw headers
  headerIter    header_begin ()     { return m_headers.begin (); }
  headerIter    header_end   ()     { return m_headers.end   (); }
  constHeaderIter header_begin () const { return m_headers.begin (); }
  constHeaderIter header_end   () const { return m_headers.end   (); }

// -----------------------------------------------------------
//  Header manipulation
// -----------------------------------------------------------

//  The 'tag' part of the header is still a std::string
  bool header_exists ( const char *key ) const {
    return header_end () != find_header ( key );
    }
    
  string_type header_value  ( const char *key ) const  {
    constHeaderIter found = find_header ( key );
    if ( found == header_end ())
      throw std::runtime_error ( "'header_value' not found" );
    return found->second;
    }
  
  void set_header_value  ( const char *key, const string_type &value, bool replace = false ) {
    if ( !replace )
      m_headers.push_back ( std::make_pair ( std::string ( key ), value ));
    else {
      headerIter found = find_header ( key );
      if ( found == m_headers.end ())
        throw std::runtime_error ( "'header_value' not found - can't replace" );
      found->second = value;
      }
    }

    
  string_type get_content_type_header () const {
    constHeaderIter found = find_header ( detail::k_content_type_header );
    return found != header_end () ? found->second : m_default_content_type;
    }
  
  
  string_type get_content_type () const {
    detail::mime_content_type mct = detail::parse_content_type ( get_content_type_header ());
    return string_type ( mct.type ) + '/' + mct.sub_type;
    }
  

//  Special purpose helper routine
  void  append_phrase_to_content_type ( const char *key, const string_type &value ) {
    headerIter found = find_header ( detail::k_content_type_header );
  
  //  Create a Content-Type header if there isn't one
    if ( m_headers.end () == found ) {
      m_headers.push_back ( std::make_pair ( std::string ( detail::k_content_type_header ), m_default_content_type ));
      found = find_header ( detail::k_content_type_header );
      }
  
    detail::mime_content_type mct = detail::parse_content_type ( found->second );
    detail::phrase_container_t::const_iterator p_found =
      std::find_if ( mct.phrases.begin (), mct.phrases.end (), 
        detail::find_mime_header<std::string> ( key ));
    if ( p_found != mct.phrases.end ())
      throw std::runtime_error ( "phrase already exists" );
    found->second += str ( boost::format ( "; %s=\"%s\"" ) % key % value );
  } 
  
  
//  Body get/set methods
  mimeBody body ()    const { return m_body; }
  mimeBody body_prolog () const { return m_body; }
  mimeBody body_epilog () const { return m_body_epilog; }

  std::size_t body_size () const { return m_body->size (); }

  template <typename Iterator>
  void set_body ( Iterator begin, Iterator end ) {
    bodyContainer temp;
    std::copy ( begin, end, std::back_inserter ( temp ));
    m_body->swap ( temp );
    }

  void set_body ( const char *contents, size_t sz ) { set_body ( contents, contents + sz ); }
  void set_body ( std::istream &in )                { set_body ( std::istream_iterator<char> ( in ), std::istream_iterator<char> ()); }
  void set_body ( const bodyContainer &new_body )   { *m_body = new_body; }

  void set_multipart_prolog_is_missing ( bool isMissing ) { m_body_prolog_is_missing = isMissing; }
  void set_body_prolog ( const bodyContainer &new_body_prolog ) { *m_body        = new_body_prolog; }
  void set_body_epilog ( const bodyContainer &new_body_epilog ) { *m_body_epilog = new_body_epilog; }

// -----------------------------------------------------------
//  Output
// -----------------------------------------------------------
  void stream_out ( std::ostream &out ) { // called by operator << 
    if ( m_part_kind == simple_part ) {
      detail::write_headers ( out, m_headers );
      detail::write_body ( out, *m_body );
      }
    else if ( m_part_kind == message_part ) {
      if ( m_subparts.size () != 1 )
        throw std::runtime_error ( "message part w/wrong number of sub-parts - should be 1" );
        
      detail::write_headers ( out, m_headers );
      m_subparts [ 0 ]->stream_out ( out );
      }
    else { // multi-part
    //  Find or invent a boundary string
      std::string boundary;
      try { boundary = detail::get_boundary ( get_content_type_header ()); }
      catch ( std::runtime_error & ) {
      //  FIXME: Make boundary strings (more?) unique
        boundary = str ( boost::format ( "------=_NextPart-%s.%08ld" ) % detail::k_package_name % std::clock ());
        append_phrase_to_content_type ( "boundary", boundary );
        }
      
    //  If the body prolog is missing, we don't want a CRLF on the front of the first sub-part.
    //  Note that there's a (subtle) difference between an zero length body and a missing one.
    //  See the comments in the parser code for more information.
      detail::write_headers ( out, m_headers );
      bool writeCR = body_prolog ()->size () > 0 || !m_body_prolog_is_missing;
      detail::write_body ( out, *body_prolog ());
      for ( typename partList::const_iterator iter = m_subparts.begin (); iter != m_subparts.end (); ++iter ) {
        detail::write_boundary ( out, boundary, false, writeCR );
        (*iter)->stream_out ( out );
        writeCR = true;
        }
      detail::write_boundary ( out, boundary, true );
      detail::write_body ( out, *body_epilog ());
      }
  //  out << detail::k_crlf;
    }
  
//  Build a simple mime part
  template <typename Iterator>
  static basic_mime make_simple_part ( const char *type, const char *subtype, Iterator begin, Iterator end ) {
    basic_mime retval ( type, subtype );
    retval.set_body ( begin, end );
    return retval;
    }

//  Build a mime part from a pair of iterators
  template <typename Iterator>
  static boost::shared_ptr< basic_mime<traits> > parse_mime ( Iterator &begin, Iterator end ) {
    return detail::parse_mime<Iterator, traits> ( begin, end );
    }

//  Build a mime part from a stream
  static boost::shared_ptr < basic_mime > parse_mime ( std::istream &in ) {
    boost::spirit::istream_iterator first (in);
    boost::spirit::istream_iterator last;
    return parse_mime ( first, last );
    }
    

private:
  basic_mime ();  // Can't create a part w/o a type

  headerIter  find_header ( const char *key ) {
    return std::find_if ( header_begin (), header_end (), detail::find_mime_header<string_type> ( key ));
    }
  
  constHeaderIter find_header ( const char *key ) const {
    return std::find_if ( header_begin (), header_end (), detail::find_mime_header<string_type> ( key ));
    }

  static part_kind part_kind_from_string_pair ( const std::string &type, const std::string &sub_type ) {
    if ( boost::iequals ( type, "multipart" ))
      return multi_part;
      
    part_kind retVal = simple_part;
  //  I expect that this will get more complicated as time goes on....
  //
  //  message/delivery-status is a simple type.
  //  RFC 3464 defines message/delivery-status <http://www.faqs.org/rfcs/rfc3464.html>
  //  The body of a message/delivery-status consists of one or more
  //     "fields" formatted according to the ABNF of RFC 822 header "fields"
  //     (see [RFC822]).  
    if ( boost::iequals ( type, "message" ))
      if ( !boost::iequals ( sub_type, "delivery-status" ))
        retVal = message_part;
    return retVal;
    }
  
  void check_subpart_index ( size_t idx ) const {
    if ( get_part_kind () == simple_part )
      throw std::runtime_error ( "Simple Mime parts don't have sub-parts" );
    else if ( get_part_kind () == multi_part ) {
        if ( idx >= m_subparts.size ())
          throw std::runtime_error ( 
            str ( boost::format ( "Trying to access part %d (of %d) sub-part to a multipart/xxx mime part" ) % idx %  m_subparts.size ()));
      }
    else {  // message-part
      if ( get_part_kind () == message_part )
        if ( m_subparts.size () > 1 )
          throw std::runtime_error ( "How did a message/xxx mime parts get more than one sub-part?" );

      if ( idx >= m_subparts.size ())
        throw std::runtime_error ( 
          str ( boost::format ( "Trying to access part %d (of %d) sub-part to a message/xxx mime part" ) % idx %  m_subparts.size ()));
      }
    }

  void check_subpart_append () const {
    if ( get_part_kind () == simple_part )
      throw std::runtime_error ( "Simple Mime parts don't have sub-parts" );
    else if ( get_part_kind () == message_part ) {
      if ( m_subparts.size () > 0 )
        throw std::runtime_error ( "Can't add a second sub-part to a message/xxx mime part" );
      }
  //  else { /* Multi-part */ } // We can always add to a multi-part
    }
  
  part_kind m_part_kind;
  headerList  m_headers;
  bool    m_body_prolog_is_missing; // only for multiparts
  mimeBody  m_body;
  mimeBody  m_body_epilog;        // only for multiparts
  partList  m_subparts;         // only for multiparts or message
  string_type m_default_content_type;
  };


namespace detail {

  template<typename Iterator, typename traits>
  static boost::shared_ptr< basic_mime<traits> > parse_mime ( Iterator &begin, Iterator end, const char *default_content_type ) {
    tracer t ( __func__ );
    typedef typename boost::mime::basic_mime<traits>  mime_part;
    
    shared_ptr < mime_part > retVal (
      new mime_part ( detail::read_headers<typename mime_part::headerList> ( begin, end ), default_content_type ));
  
    std::string content_type = retVal->get_content_type ();

#ifdef  DUMP_MIME_DATA
    std::cout << "Content-Type: " << content_type << std::endl;
    std::cout << str ( boost::format ( "retVal->get_part_kind () = %d" ) % ((int) retVal->get_part_kind ())) << std::endl;
#endif

    if ( retVal->get_part_kind () == mime_part::simple_part )
      retVal->set_body ( detail::read_simplepart_body<typename mime_part::bodyContainer, Iterator> ( begin, end ));
    else if ( retVal->get_part_kind () == mime_part::message_part ) {
    //  If we've got a message/xxxx, then there is no body, and we have a single
    //  embedded mime_part (which, of course, could be a multipart)
      retVal->append_part ( parse_mime<Iterator, traits> ( begin, end ));
      }
    else /* multi_part */ {
    //  Find or invent a boundary string
      std::string part_separator = detail::get_boundary ( retVal->get_content_type_header ());
      const char *cont_type = boost::iequals ( content_type, "multipart/digest" ) ? "message/rfc822" : "text/plain";
      
      detail::multipart_body_type<typename traits::body_type> body_and_subParts;
      detail::read_multipart_body ( begin, end, body_and_subParts, part_separator );

      retVal->set_body_prolog ( body_and_subParts.body_prolog );
      retVal->set_multipart_prolog_is_missing ( body_and_subParts.prolog_is_missing );
      for ( typename sub_parts_t::const_iterator iter = body_and_subParts.sub_parts.begin ();
          iter != body_and_subParts.sub_parts.end (); ++iter ) {
        typedef typename sub_part_t::const_iterator iter_type;
        iter_type b = iter->begin ();
        iter_type e = iter->end ();
        retVal->append_part ( parse_mime<iter_type, traits> ( b, e, cont_type ));
        }
      retVal->set_body_epilog ( body_and_subParts.body_epilog );
      }
    
    return retVal;
    } 
  
  }


// -----------------------------------------------------------
//
//  Streaming
//
// -----------------------------------------------------------

template<typename traits>
inline std::ostream & operator << ( std::ostream &stream, basic_mime<traits> &part ) {
  part.stream_out ( stream );
  return stream;
  }

template<typename traits>
inline std::ostream & operator << ( std::ostream &stream, boost::shared_ptr <basic_mime<traits> > part ) {
  return stream << *part;
  }


    
}}

BOOST_FUSION_ADAPT_STRUCT(
   boost::mime::detail::mime_content_type,
   (std::string, type)
   (std::string, sub_type)
   (boost::mime::detail::phrase_container_t, phrases)
)

#endif  // _BOOST_MIME_HPP
