// Copyright (c) 2014 Baidu, Inc.
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//     http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef BUTIL_RAW_PACK_H
#define BUTIL_RAW_PACK_H

#include "butil/sys_byteorder.h"

namespace butil {

// -------------------------------------------------------------------------
// NOTE: RawPacker/RawUnpacker is used for packing/unpacking low-level and
// hard-to-change header. If the fields are likely to be changed in future,
// use protobuf.
// -------------------------------------------------------------------------

// This utility class packs 32-bit and 64-bit integers into binary data 
// that can be unpacked by RawUnpacker. Notice that the packed data is 
// schemaless and user must match pack..() methods with same-width 
// unpack..() methods to get the integers back.
// Example:
//   char buf[16];  // 4 + 8 + 4 bytes.
//   butil::RawPacker(buf).pack32(a).pack64(b).pack32(c);  // buf holds packed data
//
//   ... network ...
//
//   // positional correspondence with pack..()
//   butil::Unpacker(buf2).unpack32(a).unpack64(b).unpack32(c);
class RawPacker {
public:
    // Notice: User must guarantee `stream' is as long as the packed data.
    explicit RawPacker(void* stream) : _stream((char*)stream) {}
    ~RawPacker() {}

    // Not using operator<< because some values may be packed differently from
    // its type.
    RawPacker& pack32(uint32_t host_value) {
        *(uint32_t*)_stream = HostToNet32(host_value);
        _stream += 4;
        return *this;
    }

    RawPacker& pack64(uint64_t host_value) {
        uint32_t *p = (uint32_t*)_stream;
        p[0] = HostToNet32(host_value >> 32);
        p[1] = HostToNet32(host_value & 0xFFFFFFFF);
        _stream += 8;
        return *this;
    }

private:
    char* _stream;
};

// This utility class unpacks 32-bit and 64-bit integers from binary data
// packed by RawPacker.
class RawUnpacker {
public:
    explicit RawUnpacker(const void* stream) : _stream((const char*)stream) {}
    ~RawUnpacker() {}

    RawUnpacker& unpack32(uint32_t & host_value) {
        host_value = NetToHost32(*(const uint32_t*)_stream);
        _stream += 4;
        return *this;
    }

    RawUnpacker& unpack64(uint64_t & host_value) {
        const uint32_t *p = (const uint32_t*)_stream;
        host_value = (((uint64_t)NetToHost32(p[0])) << 32) | NetToHost32(p[1]);
        _stream += 8;
        return *this;
    }

private:
    const char* _stream;
};

}  // namespace butil

#endif  // BUTIL_RAW_PACK_H
