376 lines
11 KiB
C++
376 lines
11 KiB
C++
/*
|
|
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
|
Released under Apache 2.0 license as described in the file LICENSE.
|
|
|
|
Author: Leonardo de Moura
|
|
*/
|
|
#include <vector>
|
|
#include <string>
|
|
#include <limits>
|
|
#include <stdio.h>
|
|
#include <utility>
|
|
#include <ios>
|
|
#include "runtime/exception.h"
|
|
#include "runtime/serializer.h"
|
|
#include "runtime/object.h"
|
|
#include "runtime/mpz.h"
|
|
|
|
namespace lean {
|
|
void initialize_serializer() {
|
|
}
|
|
|
|
void finalize_serializer() {
|
|
}
|
|
|
|
serializer::~serializer() {
|
|
for (std::pair<object*, unsigned> const & it : m_obj_table) {
|
|
dec_ref(it.first);
|
|
}
|
|
}
|
|
|
|
void serializer::write_unsigned_short(unsigned short i) {
|
|
static_assert(sizeof(i) == 2, "unexpected unsigned short size");
|
|
m_out.put((i >> 8) & 0xff);
|
|
m_out.put(i & 0xff);
|
|
}
|
|
|
|
void serializer::write_unsigned(unsigned i) {
|
|
static_assert(sizeof(i) == 4, "unexpected unsigned size");
|
|
if (i < 255) {
|
|
m_out.put(i & 0xff);
|
|
} else {
|
|
m_out.put(-1);
|
|
m_out.put((i >> 24) & 0xff);
|
|
m_out.put((i >> 16) & 0xff);
|
|
m_out.put((i >> 8) & 0xff);
|
|
m_out.put(i & 0xff);
|
|
}
|
|
}
|
|
|
|
void serializer::write_uint64(uint64 i) {
|
|
static_assert(sizeof(i) == 8, "unexpected uint64 size");
|
|
write_unsigned((i >> 32) & 0xffffffff);
|
|
write_unsigned(i & 0xffffffff);
|
|
}
|
|
|
|
void serializer::write_size_t(size_t i) {
|
|
if (sizeof(i) == 8) {
|
|
write_uint64(static_cast<uint64>(i));
|
|
} else {
|
|
lean_assert(sizeof(i) == 4);
|
|
write_unsigned(static_cast<unsigned>(i));
|
|
}
|
|
}
|
|
|
|
void serializer::write_int(int i) {
|
|
static_assert(sizeof(i) == 4, "unexpected int size");
|
|
write_unsigned(i);
|
|
}
|
|
|
|
void serializer::write_blob(std::string const & s) {
|
|
write_unsigned(s.size());
|
|
m_out.write(&s[0], s.size());
|
|
}
|
|
|
|
void serializer::write_constructor(object * o) {
|
|
lean_assert(is_cnstr(o));
|
|
unsigned num_objs = cnstr_num_objs(o);
|
|
unsigned scalar_sz = cnstr_scalar_size(o);
|
|
write_unsigned_short(cnstr_tag(o));
|
|
write_unsigned_short(num_objs);
|
|
write_unsigned_short(scalar_sz);
|
|
object ** it = cnstr_obj_cptr(o);
|
|
object ** end = it + num_objs;
|
|
for (; it != end; ++it)
|
|
write_object(*it);
|
|
unsigned char * sit = cnstr_scalar_cptr(o);
|
|
unsigned char * send = sit + scalar_sz;
|
|
for (; sit != send; ++sit)
|
|
m_out.put(*sit);
|
|
}
|
|
|
|
void serializer::write_closure(object *) { // NOLINT
|
|
/* TODO(Leo): we need a table from function pointer to unique name id.
|
|
|
|
For serializing bytecode, we will need to retrieve the unique name id too.
|
|
The trick of storing the bytecode index as the first argument of an "eval"
|
|
C function is not going to work. It seems we will to store the index as
|
|
a tagged pointer and handle it at `apply`.
|
|
*/
|
|
throw exception("serializer for closures has not been implemented yet");
|
|
}
|
|
|
|
void serializer::write_thunk(object * o) {
|
|
object * r = thunk_get(o);
|
|
write_object(r);
|
|
}
|
|
|
|
void serializer::write_task(object * o) {
|
|
object * r = task_get(o);
|
|
write_object(r);
|
|
}
|
|
|
|
void serializer::write_array(object * o) {
|
|
lean_assert(is_array(o));
|
|
size_t sz = sarray_size(o);
|
|
write_size_t(sz);
|
|
object ** it = array_cptr(o);
|
|
object ** end = it + sz;
|
|
for (; it != end; ++it)
|
|
write_object(*it);
|
|
}
|
|
|
|
void serializer::write_scalar_array(object * o) {
|
|
lean_assert(is_sarray(o));
|
|
unsigned esz = sarray_elem_size(o);
|
|
size_t sz = sarray_size(o);
|
|
write_unsigned(esz);
|
|
write_size_t(sz);
|
|
size_t num_bytes = sz*esz;
|
|
unsigned char const * it = sarray_cptr_core<unsigned char>(o);
|
|
unsigned char const * end = it + num_bytes;
|
|
for (; it != end; ++it)
|
|
m_out.put(*it);
|
|
}
|
|
|
|
void serializer::write_string_object(object * o) {
|
|
size_t sz = string_size(o);
|
|
size_t len = string_len(o);
|
|
write_size_t(sz);
|
|
write_size_t(len);
|
|
char const * it = string_cstr(o);
|
|
char const * end = it + sz;
|
|
for (; it != end; ++it)
|
|
m_out.put(*it);
|
|
}
|
|
|
|
void serializer::write_mpz(mpz const & n) {
|
|
std::ostringstream out;
|
|
out << n;
|
|
write_string(out.str());
|
|
}
|
|
|
|
void serializer::write_external(object *) { // NOLINT
|
|
/* TODO(Leo): we need support for registering serializers/deserializers
|
|
for external objects.
|
|
*/
|
|
throw exception("serializer for external objects has not been implemented yet");
|
|
}
|
|
|
|
void serializer::write_object(object * o) {
|
|
if (is_scalar(o)) {
|
|
m_out.put(0);
|
|
write_unsigned(unbox(o));
|
|
} else {
|
|
auto it = m_obj_table.find(o);
|
|
if (it != m_obj_table.end()) {
|
|
m_out.put(1);
|
|
write_unsigned(it->second);
|
|
} else {
|
|
object_kind k = get_kind(o);
|
|
m_out.put(static_cast<unsigned>(k) + 2);
|
|
switch (k) {
|
|
case object_kind::Constructor: write_constructor(o); break;
|
|
case object_kind::Closure: write_closure(o); break;
|
|
case object_kind::Task: write_task(o); break;
|
|
case object_kind::Thunk: write_thunk(o); break;
|
|
case object_kind::Array: write_array(o); break;
|
|
case object_kind::ScalarArray: write_scalar_array(o); break;
|
|
case object_kind::String: write_string_object(o); break;
|
|
case object_kind::MPZ: write_mpz(mpz_value(o)); break;
|
|
case object_kind::External: write_external(o); break;
|
|
default: lean_unreachable();
|
|
}
|
|
inc_ref(o);
|
|
m_obj_table.insert(std::make_pair(o, m_obj_table.size()));
|
|
}
|
|
}
|
|
}
|
|
|
|
corrupted_stream_exception::corrupted_stream_exception():
|
|
exception("corrupted binary file") {}
|
|
|
|
void serializer::write_double(double d) {
|
|
std::ostringstream out;
|
|
// TODO(Leo): the following code may miss precision.
|
|
// We should use std::ios::hexfloat, but it is not supported by
|
|
// g++ yet.
|
|
out.flags (std::ios::scientific);
|
|
out.precision(std::numeric_limits<double>::digits10 + 1);
|
|
out << std::hex << d;
|
|
write_string(out.str());
|
|
}
|
|
|
|
deserializer::~deserializer() {
|
|
for (object * o : m_objs)
|
|
dec_ref(o);
|
|
}
|
|
|
|
std::string deserializer::read_string() {
|
|
std::string r;
|
|
while (true) {
|
|
char c = m_in.get();
|
|
if (c == 0)
|
|
break;
|
|
if (m_in.eof())
|
|
throw corrupted_stream_exception();
|
|
r += c;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
unsigned deserializer::read_unsigned_ext() {
|
|
unsigned r;
|
|
static_assert(sizeof(r) == 4, "unexpected unsigned size");
|
|
r = static_cast<unsigned>(m_in.get()) << 24;
|
|
r |= static_cast<unsigned>(m_in.get()) << 16;
|
|
r |= static_cast<unsigned>(m_in.get()) << 8;
|
|
r |= static_cast<unsigned>(m_in.get());
|
|
return r;
|
|
}
|
|
|
|
unsigned short deserializer::read_unsigned_short() {
|
|
unsigned short r;
|
|
static_assert(sizeof(r) == 2, "unexpected unsigned short size");
|
|
r = static_cast<unsigned short>(m_in.get()) << 8;
|
|
r |= static_cast<unsigned short>(m_in.get());
|
|
return r;
|
|
}
|
|
|
|
uint64 deserializer::read_uint64() {
|
|
uint64 r;
|
|
static_assert(sizeof(r) == 8, "unexpected uint64 size");
|
|
r = static_cast<uint64>(read_unsigned()) << 32;
|
|
r |= static_cast<uint64>(read_unsigned());
|
|
return r;
|
|
}
|
|
|
|
size_t deserializer::read_size_t() {
|
|
if (sizeof(size_t) == 8) {
|
|
return static_cast<size_t>(read_uint64());
|
|
} else {
|
|
lean_assert(sizeof(size_t) == 4);
|
|
return static_cast<size_t>(read_unsigned());
|
|
}
|
|
}
|
|
|
|
double deserializer::read_double() {
|
|
// TODO(Leo): use std::hexfloat as soon as it is supported by g++
|
|
std::istringstream in(read_string());
|
|
double r;
|
|
in >> r;
|
|
return r;
|
|
}
|
|
|
|
mpz deserializer::read_mpz() {
|
|
return mpz(read_string().c_str());
|
|
}
|
|
|
|
std::string deserializer::read_blob() {
|
|
unsigned sz = read_unsigned();
|
|
std::string s(sz, '\0');
|
|
m_in.read(&s[0], sz);
|
|
return s;
|
|
}
|
|
|
|
object * deserializer::read_constructor() {
|
|
unsigned tag = read_unsigned_short();
|
|
unsigned num_objs = read_unsigned_short();
|
|
unsigned scalar_sz = read_unsigned_short();
|
|
object * r = alloc_cnstr(tag, num_objs, scalar_sz);
|
|
for (unsigned i = 0; i < num_objs; i++) {
|
|
object * o = read_object();
|
|
inc(o);
|
|
cnstr_set(r, i, o);
|
|
}
|
|
unsigned char * it = cnstr_scalar_cptr(r);
|
|
unsigned char * end = it + scalar_sz;
|
|
for (; it != end; ++it)
|
|
*it = m_in.get();
|
|
return r;
|
|
}
|
|
|
|
object * deserializer::read_closure() {
|
|
throw exception("serializer for closures has not been implemented yet");
|
|
}
|
|
|
|
object * deserializer::read_thunk() {
|
|
object * v = read_object();
|
|
inc(v);
|
|
return thunk_pure(v);
|
|
}
|
|
|
|
object * deserializer::read_task() {
|
|
object * v = read_object();
|
|
inc(v);
|
|
return task_pure(v);
|
|
}
|
|
|
|
object * deserializer::read_array() {
|
|
size_t sz = read_size_t();
|
|
object * r = alloc_array(sz, sz);
|
|
object ** it = array_cptr(r);
|
|
object ** end = it + sz;
|
|
for (; it != end; ++it) {
|
|
object * o = read_object();
|
|
inc(o);
|
|
*it = o;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
object * deserializer::read_scalar_array() {
|
|
unsigned esz = read_unsigned();
|
|
size_t sz = read_size_t();
|
|
object * r = alloc_sarray(esz, sz, sz);
|
|
unsigned char * it = sarray_cptr_core<unsigned char>(r);
|
|
unsigned char * end = it + sz*esz;
|
|
for (; it != end; ++it)
|
|
*it = m_in.get();
|
|
return r;
|
|
}
|
|
|
|
object * deserializer::read_string_object() {
|
|
size_t sz = read_size_t();
|
|
size_t len = read_size_t();
|
|
object * r = alloc_string(sz, sz, len);
|
|
unsigned char * it = const_cast<unsigned char*>(reinterpret_cast<unsigned char const *>(string_cstr(r)));
|
|
unsigned char * end = it + sz;
|
|
for (; it != end; ++it)
|
|
*it = m_in.get();
|
|
return r;
|
|
}
|
|
|
|
object * deserializer::read_external() {
|
|
throw exception("serializer for external objects has not been implemented yet");
|
|
}
|
|
|
|
object * deserializer::read_object() {
|
|
unsigned c = m_in.get();
|
|
if (c == 0) {
|
|
return box(read_unsigned());
|
|
} else if (c == 1) {
|
|
unsigned i = read_unsigned();
|
|
if (i >= m_objs.size())
|
|
throw corrupted_stream_exception();
|
|
return m_objs[i];
|
|
} else {
|
|
object_kind k = static_cast<object_kind>(c - 2);
|
|
object * r;
|
|
switch (k) {
|
|
case object_kind::Constructor: r = read_constructor(); break;
|
|
case object_kind::Closure: r = read_closure(); break;
|
|
case object_kind::Thunk: r = read_thunk(); break;
|
|
case object_kind::Task: r = read_task(); break;
|
|
case object_kind::Array: r = read_array(); break;
|
|
case object_kind::ScalarArray: r = read_scalar_array(); break;
|
|
case object_kind::String: r = read_string_object(); break;
|
|
case object_kind::MPZ: r = alloc_mpz(read_mpz()); break;
|
|
case object_kind::External: r = read_external(); break;
|
|
default: throw corrupted_stream_exception();
|
|
}
|
|
m_objs.push_back(r);
|
|
return r;
|
|
}
|
|
}
|
|
}
|