/**
* Copyright (c) 2018 Cornell University.
*
* Author: Ted Yin <tederminant@gmail.com>
*
* 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.
*/
#ifndef _SALTICIDAE_STREAM_H
#define _SALTICIDAE_STREAM_H
#include "salticidae/type.h"
#include "salticidae/crypto.h"
namespace salticidae {
template<size_t N, typename T> class Blob;
using uint256_t = Blob<256, uint64_t>;
class DataStream {
bytearray_t buffer;
size_t offset;
public:
DataStream(): offset(0) {}
DataStream(const uint8_t *begin, const uint8_t *end): buffer(begin, end), offset(0) {}
DataStream(bytearray_t &&data): buffer(std::move(data)), offset(0) {}
DataStream(const bytearray_t &data): buffer(data), offset(0) {}
DataStream(DataStream &&other):
buffer(std::move(other.buffer)),
offset(other.offset) {}
DataStream(const DataStream &other):
buffer(other.buffer),
offset(other.offset) {}
DataStream &operator=(const DataStream &other) {
buffer = other.buffer;
offset = other.offset;
return *this;
}
DataStream &operator=(DataStream &&other) {
buffer = std::move(other.buffer);
offset = other.offset;
return *this;
}
uint8_t *data() { return &buffer[offset]; }
void clear() {
buffer.clear();
offset = 0;
}
size_t size() const {
return buffer.size() - offset;
}
template<typename T>
typename std::enable_if<std::is_integral<T>::value, DataStream &>::type
operator<<(T d) {
buffer.resize(buffer.size() + sizeof(T));
*(reinterpret_cast<T *>(&*buffer.end() - sizeof(T))) = d;
return *this;
}
template<typename T>
typename std::enable_if<is_ranged<T>::value, DataStream &>::type
operator<<(const T &d) {
buffer.insert(buffer.end(), d.begin(), d.end());
return *this;
}
void put_data(const uint8_t *begin, const uint8_t *end) {
size_t len = end - begin;
buffer.resize(buffer.size() + len);
memmove(&*buffer.end() - len, begin, len);
}
const uint8_t *get_data_inplace(size_t len) {
auto res = (uint8_t *)&*(buffer.begin() + offset);
#ifndef SALTICIDAE_NOCHECK
if (offset + len > buffer.size())
throw std::ios_base::failure("insufficient buffer");
#endif
offset += len;
return res;
}
template<typename T>
typename std::enable_if<!is_ranged<T>::value &&
!std::is_integral<T>::value, DataStream &>::type
operator<<(const T &obj) {
obj.serialize(*this);
return *this;
}
DataStream &operator<<(const char *cstr) {
put_data((uint8_t *)cstr, (uint8_t *)cstr + strlen(cstr));
return *this;
}
template<typename T>
typename std::enable_if<std::is_integral<T>::value, DataStream &>::type
operator>>(T &d) {
#ifndef SALTICIDAE_NOCHECK
if (offset + sizeof(T) > buffer.size())
throw std::ios_base::failure("insufficient buffer");
#endif
d = *(reinterpret_cast<T *>(&buffer[offset]));
offset += sizeof(T);
return *this;
}
template<typename T>
typename std::enable_if<!std::is_integral<T>::value, DataStream &>::type
operator>>(T &obj) {
obj.unserialize(*this);
return *this;
}
std::string get_hex() const {
char buf[3];
DataStream s;
for (auto it = buffer.begin() + offset; it != buffer.end(); it++)
{
sprintf(buf, "%02x", *it);
s.put_data((uint8_t *)buf, (uint8_t *)buf + 2);
}
return std::string(s.buffer.begin(), s.buffer.end());
}
void load_hex(const std::string &hex_str) {
size_t len = hex_str.size();
const char *p;
uint8_t *bp;
unsigned int tmp;
if (len & 1)
throw std::invalid_argument("not a valid hex string");
buffer.resize(len >> 1);