//
// MessagePack for C++ serializing routine
//
// Copyright (C) 2008-2016 FURUHASHI Sadayuki and KONDO Takatoshi
//
//    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 MSGPACK_V1_PACK_HPP
#define MSGPACK_V1_PACK_HPP

#include "rpc/msgpack/v1/pack_decl.hpp"

#include <stdexcept>
#include <limits>
#include <cstring>
#include <climits>

namespace clmdep_msgpack {

/// @cond
MSGPACK_API_VERSION_NAMESPACE(v1) {
/// @endcond

/// The class template that supports continuous packing.
/**
 * @tparam Stream  Any type that have a member function `Stream write(const char*, size_t s)`
 *
 */
template <typename Stream>
class packer {
public:
    /// Constructor
    /**
     * This constructor is left for compatibility.
     * Use `packer(Stream& s)` instead of the constructor.
     *
     * @param s A pointer to packing destination stream object.
     */
    packer(Stream* s);
    /// Constructor
    /**
     * @param s Packing destination stream object.
     */
    packer(Stream& s);

public:
    /// Packing function template
    /**
     * @tparam T The type of packing object.
     *
     * @param v a packing object.
     *
     * @return The reference of `*this`.
     */
    template <typename T>
    packer<Stream>& pack(const T& v);

    /// Packing uint8
    /**
     * The byte size of the packed data depends on `d`.
     * The packed type is positive fixnum or uint8.
     * The minimum byte size expression is used.
     * See https://github.com/msgpack/msgpack/blob/master/spec.md#formats-int
     *
     * @param d a packing object.
     *
     * @return The reference of `*this`.
     */
    packer<Stream>& pack_uint8(uint8_t d);

    /// Packing uint16
    /**
     * The byte size of the packed data depends on `d`.
     * The packed type is positive fixnum, uint8 or uint16.
     * The minimum byte size expression is used.
     * See https://github.com/msgpack/msgpack/blob/master/spec.md#formats-int
     *
     * @param d a packing object.
     *
     * @return The reference of `*this`.
     */
    packer<Stream>& pack_uint16(uint16_t d);

    /// Packing uint32
    /**
     * The byte size of the packed data depends on `d`.
     * The packed type is positive fixnum, uint8, uint16 or uint32.
     * The minimum byte size expression is used.
     * See https://github.com/msgpack/msgpack/blob/master/spec.md#formats-int
     *
     * @param d a packing object.
     *
     * @return The reference of `*this`.
     */
    packer<Stream>& pack_uint32(uint32_t d);

    /// Packing uint16
    /**
     * The byte size of the packed data depends on `d`.
     * The packed type is positive fixnum, uint8, uint16, uint32 or uint64.
     * The minimum byte size expression is used.
     * positive fixnum, uint8, uint16, or uint32 is used.
     * See https://github.com/msgpack/msgpack/blob/master/spec.md#formats-int
     *
     * @param d a packing object.
     *
     * @return The reference of `*this`.
     */
    packer<Stream>& pack_uint64(uint64_t d);

    /// Packing int8
    /**
     * The byte size of the packed data depends on `d`.
     * If `d` is zero or positive, the packed type is positive fixnum, or uint8,
     * else the packed type is negative fixnum, or int8
     * The minimum byte size expression is used.
     * See https://github.com/msgpack/msgpack/blob/master/spec.md#formats-int
     *
     * @param d a packing object.
     *
     * @return The reference of `*this`.
     */
    packer<Stream>& pack_int8(int8_t d);

    /// Packing int16
    /**
     * The byte size of the packed data depends on `d`.
     * If `d` is zero or positive, the packed type is positive fixnum, uint8, or uint16,
     * else the packed type is negative fixnum, int8, or int16.
     * The minimum byte size expression is used.
     * See https://github.com/msgpack/msgpack/blob/master/spec.md#formats-int
     *
     * @param d a packing object.
     *
     * @return The reference of `*this`.
     */
    packer<Stream>& pack_int16(int16_t d);

    /// Packing int32
    /**
     * The byte size of the packed data depends on `d`.
     * If `d` is zero or positive, the packed type is positive fixnum, uint8, uint16, or uint32,
     * else the packed type is negative fixnum, int8, int16, or int32.
     * The minimum byte size expression is used.
     * See https://github.com/msgpack/msgpack/blob/master/spec.md#formats-int
     *
     * @param d a packing object.
     *
     * @return The reference of `*this`.
     */
    packer<Stream>& pack_int32(int32_t d);

    /// Packing int32
    /**
     * The byte size of the packed data depends on `d`.
     * If `d` is zero or positive, the packed type is positive fixnum, uint8, uint16, uint32, or uint64,
     * else the packed type is negative fixnum, int8, int16, int32, or int64.
     * The minimum byte size expression is used.
     * See https://github.com/msgpack/msgpack/blob/master/spec.md#formats-int
     *
     * @param d a packing object.
     *
     * @return The reference of `*this`.
     */
    packer<Stream>& pack_int64(int64_t d);



    /// Packing uint8 (fixed packed type).
    /**
     * The packed type is always uint8.
     * See https://github.com/msgpack/msgpack/blob/master/spec.md#formats-int
     *
     * @param d a packing object.
     *
     * @return The reference of `*this`.
     */
    packer<Stream>& pack_fix_uint8(uint8_t d);

    /// Packing uint8 (fixed packed type).
    /**
     * The packed type is always uint16.
     * See https://github.com/msgpack/msgpack/blob/master/spec.md#formats-int
     *
     * @param d a packing object.
     *
     * @return The reference of `*this`.
     */
    packer<Stream>& pack_fix_uint16(uint16_t d);

    /// Packing uint8 (fixed packed type).
    /**
     * The packed type is always uint32.
     * See https://github.com/msgpack/msgpack/blob/master/spec.md#formats-int
     *
     * @param d a packing object.
     *
     * @return The reference of `*this`.
     */
    packer<Stream>& pack_fix_uint32(uint32_t d);

    /// Packing uint8 (fixed packed type).
    /**
     * The packed type is always uint64.
     * See https://github.com/msgpack/msgpack/blob/master/spec.md#formats-int
     *
     * @param d a packing object.
     *
     * @return The reference of `*this`.
     */
    packer<Stream>& pack_fix_uint64(uint64_t d);

    /// Packing uint8 (fixed packed type).
    /**
     * The packed type is always int8.
     * See https://github.com/msgpack/msgpack/blob/master/spec.md#formats-int
     *
     * @param d a packing object.
     *
     * @return The reference of `*this`.
     */
    packer<Stream>& pack_fix_int8(int8_t d);

    /// Packing uint8 (fixed packed type).
    /**
     * The packed type is always int16.
     * See https://github.com/msgpack/msgpack/blob/master/spec.md#formats-int
     *
     * @param d a packing object.
     *
     * @return The reference of `*this`.
     */
    packer<Stream>& pack_fix_int16(int16_t d);

    /// Packing uint8 (fixed packed type).
    /**
     * The packed type is always int32.
     * See https://github.com/msgpack/msgpack/blob/master/spec.md#formats-int
     *
     * @param d a packing object.
     *
     * @return The reference of `*this`.
     */
    packer<Stream>& pack_fix_int32(int32_t d);

    /// Packing uint8 (fixed packed type).
    /**
     * The packed type is always int64.
     * See https://github.com/msgpack/msgpack/blob/master/spec.md#formats-int
     *
     * @param d a packing object.
     *
     * @return The reference of `*this`.
     */
    packer<Stream>& pack_fix_int64(int64_t d);


    /// Packing char
    /**
     * The byte size of the packed data depends on `d`.
     * If `d` is zero or positive, the packed type is positive fixnum, or uint*,
     * else the packed type is negative fixnum, or int*
     * The minimum byte size expression is used.
     * See https://github.com/msgpack/msgpack/blob/master/spec.md#formats-int
     *
     * @param d a packing object.
     *
     * @return The reference of `*this`.
     */
    packer<Stream>& pack_char(char d);

    /// Packing signed char
    /**
     * The byte size of the packed data depends on `d`.
     * If `d` is zero or positive, the packed type is positive fixnum, or uint*,
     * else the packed type is negative fixnum, or int*
     * The minimum byte size expression is used.
     * See https://github.com/msgpack/msgpack/blob/master/spec.md#formats-int
     *
     * @param d a packing object.
     *
     * @return The reference of `*this`.
     */
    packer<Stream>& pack_signed_char(signed char d);

    /// Packing short
    /**
     * The byte size of the packed data depends on `d`.
     * If `d` is zero or positive, the packed type is positive fixnum, or uint*,
     * else the packed type is negative fixnum, or int*
     * The minimum byte size expression is used.
     * See https://github.com/msgpack/msgpack/blob/master/spec.md#formats-int
     *
     * @param d a packing object.
     *
     * @return The reference of `*this`.
     */
    packer<Stream>& pack_short(short d);

    /// Packing int
    /**
     * The byte size of the packed data depends on `d`.
     * If `d` is zero or positive, the packed type is positive fixnum, or uint*,
     * else the packed type is negative fixnum, or int*
     * The minimum byte size expression is used.
     * See https://github.com/msgpack/msgpack/blob/master/spec.md#formats-int
     *
     * @param d a packing object.
     *
     * @return The reference of `*this`.
     */
    packer<Stream>& pack_int(int d);

    /// Packing long
    /**
     * The byte size of the packed data depends on `d`.
     * If `d` is zero or positive, the packed type is positive fixnum, or uint*,
     * else the packed type is negative fixnum, or int*
     * The minimum byte size expression is used.
     * See https://github.com/msgpack/msgpack/blob/master/spec.md#formats-int
     *
     * @param d a packing object.
     *
     * @return The reference of `*this`.
     */
    packer<Stream>& pack_long(long d);

    /// Packing long long
    /**
     * The byte size of the packed data depends on `d`.
     * If `d` is zero or positive, the packed type is positive fixnum, or uint*,
     * else the packed type is negative fixnum, or int*
     * The minimum byte size expression is used.
     * See https://github.com/msgpack/msgpack/blob/master/spec.md#formats-int
     *
     * @param d a packing object.
     *
     * @return The reference of `*this`.
     */
    packer<Stream>& pack_long_long(long long d);


    /// Packing unsigned char
    /**
     * The byte size of the packed data depends on `d`.
     * The packed type is positive fixnum, or uint*.
     * The minimum byte size expression is used.
     * See https://github.com/msgpack/msgpack/blob/master/spec.md#formats-int
     *
     * @param d a packing object.
     *
     * @return The reference of `*this`.
     */
    packer<Stream>& pack_unsigned_char(unsigned char d);

    /// Packing unsigned short
    /**
     * The byte size of the packed data depends on `d`.
     * The packed type is positive fixnum, or uint*.
     * The minimum byte size expression is used.
     * See https://github.com/msgpack/msgpack/blob/master/spec.md#formats-int
     *
     * @param d a packing object.
     *
     * @return The reference of `*this`.
     */
    packer<Stream>& pack_unsigned_short(unsigned short d);

    /// Packing unsigned int
    /**
     * The byte size of the packed data depends on `d`.
     * The packed type is positive fixnum, or uint*.
     * The minimum byte size expression is used.
     * See https://github.com/msgpack/msgpack/blob/master/spec.md#formats-int
     *
     * @param d a packing object.
     *
     * @return The reference of `*this`.
     */
    packer<Stream>& pack_unsigned_int(unsigned int d);

    /// Packing unsigned long
    /**
     * The byte size of the packed data depends on `d`.
     * The packed type is positive fixnum, or uint*.
     * The minimum byte size expression is used.
     * See https://github.com/msgpack/msgpack/blob/master/spec.md#formats-int
     *
     * @param d a packing object.
     *
     * @return The reference of `*this`.
     */
    packer<Stream>& pack_unsigned_long(unsigned long d);

    /// Packing unsigned long long
    /**
     * The byte size of the packed data depends on `d`.
     * The packed type is positive fixnum, or uint*.
     * The minimum byte size expression is used.
     * See https://github.com/msgpack/msgpack/blob/master/spec.md#formats-int
     *
     * @param d a packing object.
     *
     * @return The reference of `*this`.
     */
    packer<Stream>& pack_unsigned_long_long(unsigned long long d);

    /// Packing float
    /**
     * The packed type is float32.
     * See https://github.com/msgpack/msgpack/blob/master/spec.md#formats-float
     *
     * @param d a packing object.
     *
     * @return The reference of `*this`.
     */
    packer<Stream>& pack_float(float d);

    /// Packing double
    /**
     * The packed type is float64.
     * See https://github.com/msgpack/msgpack/blob/master/spec.md#formats-float
     *
     * @param d a packing object.
     *
     * @return The reference of `*this`.
     */
    packer<Stream>& pack_double(double d);


    /// Packing nil
    /**
     * The packed type is nil.
     * See https://github.com/msgpack/msgpack/blob/master/spec.md#formats-nil
     *
     * @return The reference of `*this`.
     */
    packer<Stream>& pack_nil();

    /// Packing true
    /**
     * The packed type is bool, value is true.
     * See https://github.com/msgpack/msgpack/blob/master/spec.md#bool-format-family
     *
     * @return The reference of `*this`.
     */
    packer<Stream>& pack_true();

    /// Packing false
    /**
     * The packed type is bool, value is false.
     * See https://github.com/msgpack/msgpack/blob/master/spec.md#bool-format-family
     *
     * @return The reference of `*this`.
     */
    packer<Stream>& pack_false();

    /// Packing array header and size
    /**
     * The packed type is array header and array size.
     * You need to pack `n` msgpack objects following this header and size.
     * See https://github.com/msgpack/msgpack/blob/master/spec.md#array-format-family
     *
     * @param n The number of array elements (array size).
     *
     * @return The reference of `*this`.
     */
    packer<Stream>& pack_array(uint32_t n);

    /// Packing map header and size
    /**
     * The packed type is map header and map size.
     * You need to pack `n` pairs of msgpack objects following this header and size.
     * See https://github.com/msgpack/msgpack/blob/master/spec.md#map-format-family
     *
     * @param n The number of array elements (array size).
     *
     * @return The reference of `*this`.
     */
    packer<Stream>& pack_map(uint32_t n);


    /// Packing str header and length
    /**
     * The packed type is str header and length.
     * The minimum byte size length expression is used.
     * You need to call `pack_str_body(const char* b, uint32_t l)` after this function calling with the same `l` value.
     * See https://github.com/msgpack/msgpack/blob/master/spec.md#formats-str
     *
     * @param l The length of string.
     *
     * @return The reference of `*this`.
     */
    packer<Stream>& pack_str(uint32_t l);

    /// Packing str body
    /**
     * You need to call this function just after `pack_str(uint32_t l)` calling.
     * The value `l` should be the same as `pack_str(uint32_t l)` argument `l`.
     * See https://github.com/msgpack/msgpack/blob/master/spec.md#formats-str
     *
     * @param l The length of string.
     *
     * @return The reference of `*this`.
     */
    packer<Stream>& pack_str_body(const char* b, uint32_t l);

    /// Packing raw (v4) header and length
    /**
     * The packed type is raw header and length.
     * The minimum byte size length expression is used.
     * The format raw (v4) is old MessagePack version4 format.
     * You need to call `pack_v4raw_body(const char* b, uint32_t l)` after this function calling with the same `l` value.
     * See https://github.com/msgpack/msgpack/blob/master/spec.md#formats-str
     *
     * @param l The length of string.
     *
     * @return The reference of `*this`.
     */
    packer<Stream>& pack_v4raw(uint32_t l);

    /// Packing raw (v4) body
    /**
     * The format raw (v4) is old MessagePack version4 format.
     * You need to call this function just after `pack_v4raw(uint32_t l)` calling.
     * The value `l` should be the same as `pack_v4raw(uint32_t l)` argument `l`.
     * See https://github.com/msgpack/msgpack/blob/master/spec.md#formats-str
     *
     * @param l The length of string.
     *
     * @return The reference of `*this`.
     */
    packer<Stream>& pack_v4raw_body(const char* b, uint32_t l);

    /// Packing bin header and length
    /**
     * The packed type is bin header and length.
     * The minimum byte size length expression is used.
     * You need to call `pack_bin_body(const char* b, uint32_t l)` after this function calling with the same `l` value.
     * See https://github.com/msgpack/msgpack/blob/master/spec.md#bin-format-family
     *
     * @param l The length of string.
     *
     * @return The reference of `*this`.
     */
    packer<Stream>& pack_bin(uint32_t l);

    /// Packing bin body
    /**
     * You need to call this function just after `pack_bin(uint32_t l)` calling.
     * The value `l` should be the same as `pack_bin(uint32_t l)` argument `l`.
     * See https://github.com/msgpack/msgpack/blob/master/spec.md#bin-format-family
     *
     * @param l The length of string.
     *
     * @return The reference of `*this`.
     */
    packer<Stream>& pack_bin_body(const char* b, uint32_t l);

    /// Packing ext header, type, and length
    /**
     * The packed type is ext.
     * The minimum byte size length expression is used.
     * The length 1, 2, 4, 8, and 16 can be encoded in the header.
     * You need to call `pack_ext_body(const char* b, uint32_t l)` after this function calling with the same `l` value.
     * See https://github.com/msgpack/msgpack/blob/master/spec.md#formats-ext
     *
     * @param l The length of string.
     *
     * @return The reference of `*this`.
     */
    packer<Stream>& pack_ext(size_t l, int8_t type);

    /// Packing ext body
    /**
     * You need to call this function just after `pack_ext(size_t l, int8_t type)` calling.
     * The value `l` should be the same as `pack_ext(size_t l, int8_t type)` argument `l`.
     * See https://github.com/msgpack/msgpack/blob/master/spec.md#bin-format-family
     *
     * @param l The length of string.
     *
     * @return The reference of `*this`.
     */
    packer<Stream>& pack_ext_body(const char* b, uint32_t l);

private:
    template <typename T>
    void pack_imp_uint8(T d);
    template <typename T>
    void pack_imp_uint16(T d);
    template <typename T>
    void pack_imp_uint32(T d);
    template <typename T>
    void pack_imp_uint64(T d);
    template <typename T>
    void pack_imp_int8(T d);
    template <typename T>
    void pack_imp_int16(T d);
    template <typename T>
    void pack_imp_int32(T d);
    template <typename T>
    void pack_imp_int64(T d);

    void append_buffer(const char* buf, size_t len)
        { m_stream.write(buf, len); }

private:
    Stream& m_stream;

#if defined(MSGPACK_USE_CPP03)
private:
    packer(const packer&);
    packer& operator=(const packer&);
    packer();
#else  // defined(MSGPACK_USE_CPP03)
public:
    packer(const packer&) = delete;
    packer& operator=(const packer&) = delete;
    packer() = delete;
#endif // defined(MSGPACK_USE_CPP03)
};


/// Pack the value as MessagePack format into the stream
/**
 * This function template is left for compatibility.
 * Use `void pack(Stream& s, const T& v)` instead of the function template.
 *
 * @tparam Stream Any type that have a member function `Stream write(const char*, size_t s)`
 * @tparam T Any type that is adapted to MessagePack
 * @param s The pointer to packing destination stream
 * @param v Packing value
 */
template <typename Stream, typename T>
inline void pack(Stream* s, const T& v)
{
    packer<Stream>(*s).pack(v);
}

/// Pack the value as MessagePack format into the stream
/**
 * @tparam Stream Any type that have a member function `Stream write(const char*, size_t s)`
 * @tparam T Any type that is adapted to MessagePack
 * @param s Packing destination stream
 * @param v Packing value
 */
template <typename Stream, typename T>
inline void pack(Stream& s, const T& v)
{
    packer<Stream>(s).pack(v);
}


#if MSGPACK_ENDIAN_LITTLE_BYTE
template <typename T>
inline char take8_8(T d) {
    return static_cast<char>(reinterpret_cast<uint8_t*>(&d)[0]);
}
template <typename T>
inline char take8_16(T d) {
    return static_cast<char>(reinterpret_cast<uint8_t*>(&d)[0]);
}
template <typename T>
inline char take8_32(T d) {
    return static_cast<char>(reinterpret_cast<uint8_t*>(&d)[0]);
}
template <typename T>
inline char take8_64(T d) {
    return static_cast<char>(reinterpret_cast<uint8_t*>(&d)[0]);
}

#elif MSGPACK_ENDIAN_BIG_BYTE

template <typename T>
inline char take8_8(T d) {
    return static_cast<char>(reinterpret_cast<uint8_t*>(&d)[0]);
}
template <typename T>
inline char take8_16(T d) {
    return static_cast<char>(reinterpret_cast<uint8_t*>(&d)[1]);
}
template <typename T>
inline char take8_32(T d) {
    return static_cast<char>(reinterpret_cast<uint8_t*>(&d)[3]);
}
template <typename T>
inline char take8_64(T d) {
    return static_cast<char>(reinterpret_cast<uint8_t*>(&d)[7]);
}

#else
#error msgpack-c supports only big endian and little endian
#endif

template <typename Stream>
inline packer<Stream>::packer(Stream* s) : m_stream(*s) { }

template <typename Stream>
inline packer<Stream>::packer(Stream& s) : m_stream(s) { }


template <typename Stream>
inline packer<Stream>& packer<Stream>::pack_uint8(uint8_t d)
{ pack_imp_uint8(d); return *this; }

template <typename Stream>
inline packer<Stream>& packer<Stream>::pack_uint16(uint16_t d)
{ pack_imp_uint16(d); return *this; }

template <typename Stream>
inline packer<Stream>& packer<Stream>::pack_uint32(uint32_t d)
{ pack_imp_uint32(d); return *this; }

template <typename Stream>
inline packer<Stream>& packer<Stream>::pack_uint64(uint64_t d)
{ pack_imp_uint64(d); return *this; }

template <typename Stream>
inline packer<Stream>& packer<Stream>::pack_int8(int8_t d)
{ pack_imp_int8(d); return *this; }

template <typename Stream>
inline packer<Stream>& packer<Stream>::pack_int16(int16_t d)
{ pack_imp_int16(d); return *this; }

template <typename Stream>
inline packer<Stream>& packer<Stream>::pack_int32(int32_t d)
{ pack_imp_int32(d); return *this; }

template <typename Stream>
inline packer<Stream>& packer<Stream>::pack_int64(int64_t d)
{ pack_imp_int64(d); return *this;}


template <typename Stream>
inline packer<Stream>& packer<Stream>::pack_fix_uint8(uint8_t d)
{
    char buf[2] = {static_cast<char>(0xccu), take8_8(d)};
    append_buffer(buf, 2);
    return *this;
}

template <typename Stream>
inline packer<Stream>& packer<Stream>::pack_fix_uint16(uint16_t d)
{
    char buf[3];
    buf[0] = static_cast<char>(0xcdu); _msgpack_store16(&buf[1], d);
    append_buffer(buf, 3);
    return *this;
}

template <typename Stream>
inline packer<Stream>& packer<Stream>::pack_fix_uint32(uint32_t d)
{
    char buf[5];
    buf[0] = static_cast<char>(0xceu); _msgpack_store32(&buf[1], d);
    append_buffer(buf, 5);
    return *this;
}

template <typename Stream>
inline packer<Stream>& packer<Stream>::pack_fix_uint64(uint64_t d)
{
    char buf[9];
    buf[0] = static_cast<char>(0xcfu); _msgpack_store64(&buf[1], d);
    append_buffer(buf, 9);
    return *this;
}

template <typename Stream>
inline packer<Stream>& packer<Stream>::pack_fix_int8(int8_t d)
{
    char buf[2] = {static_cast<char>(0xd0u), take8_8(d)};
    append_buffer(buf, 2);
    return *this;
}

template <typename Stream>
inline packer<Stream>& packer<Stream>::pack_fix_int16(int16_t d)
{
    char buf[3];
    buf[0] = static_cast<char>(0xd1u); _msgpack_store16(&buf[1], d);
    append_buffer(buf, 3);
    return *this;
}

template <typename Stream>
inline packer<Stream>& packer<Stream>::pack_fix_int32(int32_t d)
{
    char buf[5];
    buf[0] = static_cast<char>(0xd2u); _msgpack_store32(&buf[1], d);
    append_buffer(buf, 5);
    return *this;
}

template <typename Stream>
inline packer<Stream>& packer<Stream>::pack_fix_int64(int64_t d)
{
    char buf[9];
    buf[0] = static_cast<char>(0xd3u); _msgpack_store64(&buf[1], d);
    append_buffer(buf, 9);
    return *this;
}


template <typename Stream>
inline packer<Stream>& packer<Stream>::pack_char(char d)
{
#if defined(CHAR_MIN)
#if CHAR_MIN < 0
    pack_imp_int8(d);
#else
    pack_imp_uint8(d);
#endif
#else
#error CHAR_MIN is not defined
#endif
    return *this;
}

template <typename Stream>
inline packer<Stream>& packer<Stream>::pack_signed_char(signed char d)
{
    pack_imp_int8(d);
    return *this;
}

template <typename Stream>
inline packer<Stream>& packer<Stream>::pack_short(short d)
{
#if defined(SIZEOF_SHORT)
#if SIZEOF_SHORT == 2
    pack_imp_int16(d);
#elif SIZEOF_SHORT == 4
    pack_imp_int32(d);
#else
    pack_imp_int64(d);
#endif

#elif defined(SHRT_MAX)
#if SHRT_MAX == 0x7fff
    pack_imp_int16(d);
#elif SHRT_MAX == 0x7fffffff
    pack_imp_int32(d);
#else
    pack_imp_int64(d);
#endif

#else
    if(sizeof(short) == 2) {
        pack_imp_int16(d);
    } else if(sizeof(short) == 4) {
        pack_imp_int32(d);
    } else {
        pack_imp_int64(d);
    }
#endif
    return *this;
}

template <typename Stream>
inline packer<Stream>& packer<Stream>::pack_int(int d)
{
#if defined(SIZEOF_INT)
#if SIZEOF_INT == 2
    pack_imp_int16(d);
#elif SIZEOF_INT == 4
    pack_imp_int32(d);
#else
    pack_imp_int64(d);
#endif

#elif defined(INT_MAX)
#if INT_MAX == 0x7fff
    pack_imp_int16(d);
#elif INT_MAX == 0x7fffffff
    pack_imp_int32(d);
#else
    pack_imp_int64(d);
#endif

#else
    if(sizeof(int) == 2) {
        pack_imp_int16(d);
    } else if(sizeof(int) == 4) {
        pack_imp_int32(d);
    } else {
        pack_imp_int64(d);
    }
#endif
    return *this;
}

template <typename Stream>
inline packer<Stream>& packer<Stream>::pack_long(long d)
{
#if defined(SIZEOF_LONG)
#if SIZEOF_LONG == 2
    pack_imp_int16(d);
#elif SIZEOF_LONG == 4
    pack_imp_int32(d);
#else
    pack_imp_int64(d);
#endif

#elif defined(LONG_MAX)
#if LONG_MAX == 0x7fffL
    pack_imp_int16(d);
#elif LONG_MAX == 0x7fffffffL
    pack_imp_int32(d);
#else
    pack_imp_int64(d);
#endif

#else
    if(sizeof(long) == 2) {
        pack_imp_int16(d);
    } else if(sizeof(long) == 4) {
        pack_imp_int32(d);
    } else {
        pack_imp_int64(d);
    }
#endif
    return *this;
}

template <typename Stream>
inline packer<Stream>& packer<Stream>::pack_long_long(long long d)
{
#if defined(SIZEOF_LONG_LONG)
#if SIZEOF_LONG_LONG == 2
    pack_imp_int16(d);
#elif SIZEOF_LONG_LONG == 4
    pack_imp_int32(d);
#else
    pack_imp_int64(d);
#endif

#elif defined(LLONG_MAX)
#if LLONG_MAX == 0x7fffL
    pack_imp_int16(d);
#elif LLONG_MAX == 0x7fffffffL
    pack_imp_int32(d);
#else
    pack_imp_int64(d);
#endif

#else
    if(sizeof(long long) == 2) {
        pack_imp_int16(d);
    } else if(sizeof(long long) == 4) {
        pack_imp_int32(d);
    } else {
        pack_imp_int64(d);
    }
#endif
    return *this;
}


template <typename Stream>
inline packer<Stream>& packer<Stream>::pack_unsigned_char(unsigned char d)
{
    pack_imp_uint8(d);
    return *this;
}

template <typename Stream>
inline packer<Stream>& packer<Stream>::pack_unsigned_short(unsigned short d)
{
#if defined(SIZEOF_SHORT)
#if SIZEOF_SHORT == 2
    pack_imp_uint16(d);
#elif SIZEOF_SHORT == 4
    pack_imp_uint32(d);
#else
    pack_imp_uint64(d);
#endif

#elif defined(USHRT_MAX)
#if USHRT_MAX == 0xffffU
    pack_imp_uint16(d);
#elif USHRT_MAX == 0xffffffffU
    pack_imp_uint32(d);
#else
    pack_imp_uint64(d);
#endif

#else
    if(sizeof(unsigned short) == 2) {
        pack_imp_uint16(d);
    } else if(sizeof(unsigned short) == 4) {
        pack_imp_uint32(d);
    } else {
        pack_imp_uint64(d);
    }
#endif
    return *this;
}

template <typename Stream>
inline packer<Stream>& packer<Stream>::pack_unsigned_int(unsigned int d)
{
#if defined(SIZEOF_INT)
#if SIZEOF_INT == 2
    pack_imp_uint16(d);
#elif SIZEOF_INT == 4
    pack_imp_uint32(d);
#else
    pack_imp_uint64(d);
#endif

#elif defined(UINT_MAX)
#if UINT_MAX == 0xffffU
    pack_imp_uint16(d);
#elif UINT_MAX == 0xffffffffU
    pack_imp_uint32(d);
#else
    pack_imp_uint64(d);
#endif

#else
    if(sizeof(unsigned int) == 2) {
        pack_imp_uint16(d);
    } else if(sizeof(unsigned int) == 4) {
        pack_imp_uint32(d);
    } else {
        pack_imp_uint64(d);
    }
#endif
    return *this;
}

template <typename Stream>
inline packer<Stream>& packer<Stream>::pack_unsigned_long(unsigned long d)
{
#if defined(SIZEOF_LONG)
#if SIZEOF_LONG == 2
    pack_imp_uint16(d);
#elif SIZEOF_LONG == 4
    pack_imp_uint32(d);
#else
    pack_imp_uint64(d);
#endif

#elif defined(ULONG_MAX)
#if ULONG_MAX == 0xffffUL
    pack_imp_uint16(d);
#elif ULONG_MAX == 0xffffffffUL
    pack_imp_uint32(d);
#else
    pack_imp_uint64(d);
#endif

#else
    if(sizeof(unsigned long) == 2) {
        pack_imp_uint16(d);
    } else if(sizeof(unsigned long) == 4) {
        pack_imp_uint32(d);
    } else {
        pack_imp_uint64(d);
    }
#endif
    return *this;
}

template <typename Stream>
inline packer<Stream>& packer<Stream>::pack_unsigned_long_long(unsigned long long d)
{
#if defined(SIZEOF_LONG_LONG)
#if SIZEOF_LONG_LONG == 2
    pack_imp_uint16(d);
#elif SIZEOF_LONG_LONG == 4
    pack_imp_uint32(d);
#else
    pack_imp_uint64(d);
#endif

#elif defined(ULLONG_MAX)
#if ULLONG_MAX == 0xffffUL
    pack_imp_uint16(d);
#elif ULLONG_MAX == 0xffffffffUL
    pack_imp_uint32(d);
#else
    pack_imp_uint64(d);
#endif

#else
    if(sizeof(unsigned long long) == 2) {
        pack_imp_uint16(d);
    } else if(sizeof(unsigned long long) == 4) {
        pack_imp_uint32(d);
    } else {
        pack_imp_uint64(d);
    }
#endif
    return *this;
}


template <typename Stream>
inline packer<Stream>& packer<Stream>::pack_float(float d)
{
    union { float f; uint32_t i; } mem;
    mem.f = d;
    char buf[5];
    buf[0] = static_cast<char>(0xcau); _msgpack_store32(&buf[1], mem.i);
    append_buffer(buf, 5);
    return *this;
}

template <typename Stream>
inline packer<Stream>& packer<Stream>::pack_double(double d)
{
    union { double f; uint64_t i; } mem;
    mem.f = d;
    char buf[9];
    buf[0] = static_cast<char>(0xcbu);

#if defined(TARGET_OS_IPHONE)
    // ok
#elif defined(__arm__) && !(__ARM_EABI__) // arm-oabi
    // https://github.com/msgpack/msgpack-perl/pull/1
    mem.i = (mem.i & 0xFFFFFFFFUL) << 32UL | (mem.i >> 32UL);
#endif
    _msgpack_store64(&buf[1], mem.i);
    append_buffer(buf, 9);
    return *this;
}


template <typename Stream>
inline packer<Stream>& packer<Stream>::pack_nil()
{
    const char d = static_cast<char>(0xc0u);
    append_buffer(&d, 1);
    return *this;
}

template <typename Stream>
inline packer<Stream>& packer<Stream>::pack_true()
{
    const char d = static_cast<char>(0xc3u);
    append_buffer(&d, 1);
    return *this;
}

template <typename Stream>
inline packer<Stream>& packer<Stream>::pack_false()
{
    const char d = static_cast<char>(0xc2u);
    append_buffer(&d, 1);
    return *this;
}


template <typename Stream>
inline packer<Stream>& packer<Stream>::pack_array(uint32_t n)
{
    if(n < 16) {
        char d = static_cast<char>(0x90u | n);
        append_buffer(&d, 1);
    } else if(n < 65536) {
        char buf[3];
        buf[0] = static_cast<char>(0xdcu); _msgpack_store16(&buf[1], static_cast<uint16_t>(n));
        append_buffer(buf, 3);
    } else {
        char buf[5];
        buf[0] = static_cast<char>(0xddu); _msgpack_store32(&buf[1], static_cast<uint32_t>(n));
        append_buffer(buf, 5);
    }
    return *this;
}

template <typename Stream>
inline packer<Stream>& packer<Stream>::pack_map(uint32_t n)
{
    if(n < 16) {
        unsigned char d = static_cast<unsigned char>(0x80u | n);
        char buf = take8_8(d);
        append_buffer(&buf, 1);
    } else if(n < 65536) {
        char buf[3];
        buf[0] = static_cast<char>(0xdeu); _msgpack_store16(&buf[1], static_cast<uint16_t>(n));
        append_buffer(buf, 3);
    } else {
        char buf[5];
        buf[0] = static_cast<char>(0xdfu); _msgpack_store32(&buf[1], static_cast<uint32_t>(n));
        append_buffer(buf, 5);
    }
    return *this;
}

template <typename Stream>
inline packer<Stream>& packer<Stream>::pack_str(uint32_t l)
{
    if(l < 32) {
        unsigned char d = static_cast<uint8_t>(0xa0u | l);
        char buf = take8_8(d);
        append_buffer(&buf, 1);
    } else if(l < 256) {
        char buf[2];
        buf[0] = static_cast<char>(0xd9u); buf[1] = static_cast<uint8_t>(l);
        append_buffer(buf, 2);
    } else if(l < 65536) {
        char buf[3];
        buf[0] = static_cast<char>(0xdau); _msgpack_store16(&buf[1], static_cast<uint16_t>(l));
        append_buffer(buf, 3);
    } else {
        char buf[5];
        buf[0] = static_cast<char>(0xdbu); _msgpack_store32(&buf[1], static_cast<uint32_t>(l));
        append_buffer(buf, 5);
    }
    return *this;
}

template <typename Stream>
inline packer<Stream>& packer<Stream>::pack_str_body(const char* b, uint32_t l)
{
    append_buffer(b, l);
    return *this;
}

// Raw (V4)

template <typename Stream>
inline packer<Stream>& packer<Stream>::pack_v4raw(uint32_t l)
{
    if(l < 32) {
        unsigned char d = static_cast<uint8_t>(0xa0u | l);
        char buf = take8_8(d);
        append_buffer(&buf, 1);
    } else if(l < 65536) {
        char buf[3];
        buf[0] = static_cast<char>(0xdau); _msgpack_store16(&buf[1], static_cast<uint16_t>(l));
        append_buffer(buf, 3);
    } else {
        char buf[5];
        buf[0] = static_cast<char>(0xdbu); _msgpack_store32(&buf[1], static_cast<uint32_t>(l));
        append_buffer(buf, 5);
    }
    return *this;
}

template <typename Stream>
inline packer<Stream>& packer<Stream>::pack_v4raw_body(const char* b, uint32_t l)
{
    append_buffer(b, l);
    return *this;
}

template <typename Stream>
inline packer<Stream>& packer<Stream>::pack_bin(uint32_t l)
{
    if(l < 256) {
        char buf[2];
        buf[0] = static_cast<char>(0xc4u); buf[1] = static_cast<uint8_t>(l);
        append_buffer(buf, 2);
    } else if(l < 65536) {
        char buf[3];
        buf[0] = static_cast<char>(0xc5u); _msgpack_store16(&buf[1], static_cast<uint16_t>(l));
        append_buffer(buf, 3);
    } else {
        char buf[5];
        buf[0] = static_cast<char>(0xc6u); _msgpack_store32(&buf[1], static_cast<uint32_t>(l));
        append_buffer(buf, 5);
    }
    return *this;
}

template <typename Stream>
inline packer<Stream>& packer<Stream>::pack_bin_body(const char* b, uint32_t l)
{
    append_buffer(b, l);
    return *this;
}

template <typename Stream>
inline packer<Stream>& packer<Stream>::pack_ext(size_t l, int8_t type)
{
    switch(l) {
    case 1: {
        char buf[2];
        buf[0] = static_cast<char>(0xd4u);
        buf[1] = static_cast<char>(type);
        append_buffer(buf, 2);
    } break;
    case 2: {
        char buf[2];
        buf[0] = static_cast<char>(0xd5u);
        buf[1] = static_cast<char>(type);
        append_buffer(buf, 2);
    } break;
    case 4: {
        char buf[2];
        buf[0] = static_cast<char>(0xd6u);
        buf[1] = static_cast<char>(type);
        append_buffer(buf, 2);
    } break;
    case 8: {
        char buf[2];
        buf[0] = static_cast<char>(0xd7u);
        buf[1] = static_cast<char>(type);
        append_buffer(buf, 2);
    } break;
    case 16: {
        char buf[2];
        buf[0] = static_cast<char>(0xd8u);
        buf[1] = static_cast<char>(type);
        append_buffer(buf, 2);
    } break;
    default:
        if(l < 256) {
            char buf[3];
            buf[0] = static_cast<char>(0xc7u);
            buf[1] = static_cast<char>(l);
            buf[2] = static_cast<char>(type);
            append_buffer(buf, 3);
        } else if(l < 65536) {
            char buf[4];
            buf[0] = static_cast<char>(0xc8u);
            _msgpack_store16(&buf[1], static_cast<uint16_t>(l));
            buf[3] = static_cast<char>(type);
            append_buffer(buf, 4);
        } else {
            char buf[6];
            buf[0] = static_cast<char>(0xc9u);
            _msgpack_store32(&buf[1], static_cast<uint32_t>(l));
            buf[5] = static_cast<char>(type);
            append_buffer(buf, 6);
        }
        break;
    }
    return *this;
}

template <typename Stream>
inline packer<Stream>& packer<Stream>::pack_ext_body(const char* b, uint32_t l)
{
    append_buffer(b, l);
    return *this;
}

template <typename Stream>
template <typename T>
inline void packer<Stream>::pack_imp_uint8(T d)
{
    if(d < (1<<7)) {
        /* fixnum */
        char buf = take8_8(d);
        append_buffer(&buf, 1);
    } else {
        /* unsigned 8 */
        char buf[2] = {static_cast<char>(0xccu), take8_8(d)};
        append_buffer(buf, 2);
    }
}

template <typename Stream>
template <typename T>
inline void packer<Stream>::pack_imp_uint16(T d)
{
    if(d < (1<<7)) {
        /* fixnum */
        char buf = take8_16(d);
        append_buffer(&buf, 1);
    } else if(d < (1<<8)) {
        /* unsigned 8 */
        char buf[2] = {static_cast<char>(0xccu), take8_16(d)};
        append_buffer(buf, 2);
    } else {
        /* unsigned 16 */
        char buf[3];
        buf[0] = static_cast<char>(0xcdu); _msgpack_store16(&buf[1], static_cast<uint16_t>(d));
        append_buffer(buf, 3);
    }
}

template <typename Stream>
template <typename T>
inline void packer<Stream>::pack_imp_uint32(T d)
{
    if(d < (1<<8)) {
        if(d < (1<<7)) {
            /* fixnum */
            char buf = take8_32(d);
            append_buffer(&buf, 1);
        } else {
            /* unsigned 8 */
            char buf[2] = {static_cast<char>(0xccu), take8_32(d)};
            append_buffer(buf, 2);
        }
    } else {
        if(d < (1<<16)) {
            /* unsigned 16 */
            char buf[3];
            buf[0] = static_cast<char>(0xcdu); _msgpack_store16(&buf[1], static_cast<uint16_t>(d));
            append_buffer(buf, 3);
        } else {
            /* unsigned 32 */
            char buf[5];
            buf[0] = static_cast<char>(0xceu); _msgpack_store32(&buf[1], static_cast<uint32_t>(d));
            append_buffer(buf, 5);
        }
    }
}

template <typename Stream>
template <typename T>
inline void packer<Stream>::pack_imp_uint64(T d)
{
    if(d < (1ULL<<8)) {
        if(d < (1ULL<<7)) {
            /* fixnum */
            char buf = take8_64(d);
            append_buffer(&buf, 1);
        } else {
            /* unsigned 8 */
            char buf[2] = {static_cast<char>(0xccu), take8_64(d)};
            append_buffer(buf, 2);
        }
    } else {
        if(d < (1ULL<<16)) {
            /* unsigned 16 */
            char buf[3];
            buf[0] = static_cast<char>(0xcdu); _msgpack_store16(&buf[1], static_cast<uint16_t>(d));
            append_buffer(buf, 3);
        } else if(d < (1ULL<<32)) {
            /* unsigned 32 */
            char buf[5];
            buf[0] = static_cast<char>(0xceu); _msgpack_store32(&buf[1], static_cast<uint32_t>(d));
            append_buffer(buf, 5);
        } else {
            /* unsigned 64 */
            char buf[9];
            buf[0] = static_cast<char>(0xcfu); _msgpack_store64(&buf[1], d);
            append_buffer(buf, 9);
        }
    }
}

template <typename Stream>
template <typename T>
inline void packer<Stream>::pack_imp_int8(T d)
{
    if(d < -(1<<5)) {
        /* signed 8 */
        char buf[2] = {static_cast<char>(0xd0u), take8_8(d)};
        append_buffer(buf, 2);
    } else {
        /* fixnum */
        char buf = take8_8(d);
        append_buffer(&buf, 1);
    }
}

template <typename Stream>
template <typename T>
inline void packer<Stream>::pack_imp_int16(T d)
{
    if(d < -(1<<5)) {
        if(d < -(1<<7)) {
            /* signed 16 */
            char buf[3];
            buf[0] = static_cast<char>(0xd1u); _msgpack_store16(&buf[1], static_cast<int16_t>(d));
            append_buffer(buf, 3);
        } else {
            /* signed 8 */
            char buf[2] = {static_cast<char>(0xd0u), take8_16(d)};
            append_buffer(buf, 2);
        }
    } else if(d < (1<<7)) {
        /* fixnum */
        char buf = take8_16(d);
        append_buffer(&buf, 1);
    } else {
        if(d < (1<<8)) {
            /* unsigned 8 */
            char buf[2] = {static_cast<char>(0xccu), take8_16(d)};
            append_buffer(buf, 2);
        } else {
            /* unsigned 16 */
            char buf[3];
            buf[0] = static_cast<char>(0xcdu); _msgpack_store16(&buf[1], static_cast<uint16_t>(d));
            append_buffer(buf, 3);
        }
    }
}

template <typename Stream>
template <typename T>
inline void packer<Stream>::pack_imp_int32(T d)
{
    if(d < -(1<<5)) {
        if(d < -(1<<15)) {
            /* signed 32 */
            char buf[5];
            buf[0] = static_cast<char>(0xd2u); _msgpack_store32(&buf[1], static_cast<int32_t>(d));
            append_buffer(buf, 5);
        } else if(d < -(1<<7)) {
            /* signed 16 */
            char buf[3];
            buf[0] = static_cast<char>(0xd1u); _msgpack_store16(&buf[1], static_cast<int16_t>(d));
            append_buffer(buf, 3);
        } else {
            /* signed 8 */
            char buf[2] = { static_cast<char>(0xd0u), take8_32(d)};
            append_buffer(buf, 2);
        }
    } else if(d < (1<<7)) {
        /* fixnum */
        char buf = take8_32(d);
        append_buffer(&buf, 1);
    } else {
        if(d < (1<<8)) {
            /* unsigned 8 */
            char buf[2] = { static_cast<char>(0xccu), take8_32(d)};
            append_buffer(buf, 2);
        } else if(d < (1<<16)) {
            /* unsigned 16 */
            char buf[3];
            buf[0] = static_cast<char>(0xcdu); _msgpack_store16(&buf[1], static_cast<uint16_t>(d));
            append_buffer(buf, 3);
        } else {
            /* unsigned 32 */
            char buf[5];
            buf[0] = static_cast<char>(0xceu); _msgpack_store32(&buf[1], static_cast<uint32_t>(d));
            append_buffer(buf, 5);
        }
    }
}

template <typename Stream>
template <typename T>
inline void packer<Stream>::pack_imp_int64(T d)
{
    if(d < -(1LL<<5)) {
        if(d < -(1LL<<15)) {
            if(d < -(1LL<<31)) {
                /* signed 64 */
                char buf[9];
                buf[0] = static_cast<char>(0xd3u); _msgpack_store64(&buf[1], d);
                append_buffer(buf, 9);
            } else {
                /* signed 32 */
                char buf[5];
                buf[0] = static_cast<char>(0xd2u); _msgpack_store32(&buf[1], static_cast<int32_t>(d));
                append_buffer(buf, 5);
            }
        } else {
            if(d < -(1<<7)) {
                /* signed 16 */
                char buf[3];
                buf[0] = static_cast<char>(0xd1u); _msgpack_store16(&buf[1], static_cast<int16_t>(d));
                append_buffer(buf, 3);
            } else {
                /* signed 8 */
                char buf[2] = {static_cast<char>(0xd0u), take8_64(d)};
                append_buffer(buf, 2);
            }
        }
    } else if(d < (1<<7)) {
        /* fixnum */
        char buf = take8_64(d);
        append_buffer(&buf, 1);
    } else {
        if(d < (1LL<<16)) {
            if(d < (1<<8)) {
                /* unsigned 8 */
                char buf[2] = {static_cast<char>(0xccu), take8_64(d)};
                append_buffer(buf, 2);
            } else {
                /* unsigned 16 */
                char buf[3];
                buf[0] = static_cast<char>(0xcdu); _msgpack_store16(&buf[1], static_cast<uint16_t>(d));
                append_buffer(buf, 3);
            }
        } else {
            if(d < (1LL<<32)) {
                /* unsigned 32 */
                char buf[5];
                buf[0] = static_cast<char>(0xceu); _msgpack_store32(&buf[1], static_cast<uint32_t>(d));
                append_buffer(buf, 5);
            } else {
                /* unsigned 64 */
                char buf[9];
                buf[0] = static_cast<char>(0xcfu); _msgpack_store64(&buf[1], d);
                append_buffer(buf, 9);
            }
        }
    }
}

/// @cond
}  // MSGPACK_API_VERSION_NAMESPACE(v1)
/// @endcond

}  // namespace clmdep_msgpack

#endif // MSGPACK_V1_PACK_HPP
