Files
mlpack/fastlib/base/otrav.h
T
2007-06-01 23:41:42 +00:00

883 lines
29 KiB
C++

/**
* @file otrav.h
*
* Object-tree traversal.
*
* This is for traversing a directed acyclic graph of pointers, i.e. the
* actual underlying data structure. It turns out a generalized DAG
* traversal framework allows for the following to be available at no
* additional work on the application programmer:
*
* @li Serialization (save to disk)
* @li Deserialization (read from disk)
* @li Object freezing/thawing/refreezing
* (storing bulk flattened objects in RAM)
* @li Debug print, or save to s-expression or XML
* @li Destructors and copy constructors
*
*
* This has no support for (at least currently):
*
* @li Cycles
* @li Polymorphism (i.e. object-oriented inheritance)
*/
#ifndef BASE_OTRAV_H
#define BASE_OTRAV_H
#include "ccmem.h"
#include <typeinfo>
#include <stdarg.h>
#include <ctype.h>
// TODO: Remove nullability from arrays
#define OT__NAME(x) v_OT->Name( #x )
/**
* Within OT_DEF, declare a sub-object (or primitive) that is directly
* contained, NOT pointed to.
*/
#define OT_MY_OBJECT(x) (OT__NAME(x), v_OT->MyObject(this->x))
/**
* Within OT_DEF, declare a static-sized array embedded within your object.
*
* The length of the array is determined automatically via sizeof.
*/
#define OT_MY_ARRAY(x) (OT__NAME(x), v_OT->MyArray(this->x, sizeof(this->x) / sizeof(this->x[0])))
/**
* Within OT_DEF, declare an object being pointed to, managed by
* new and delete.
*/
#define OT_PTR(x) (OT__NAME(x), v_OT->Ptr(this->x, false))
/**
* Within OT_DEF, declare an array being pointed to, managed by
* new[] and delete[].
*/
#define OT_ARRAY(x, i) (OT__NAME(x), v_OT->Array(this->x, i, false))
/**
* Within OT_DEF, declare an array or object being pointed to managed by
* malloc and free.
*/
#define OT_MALLOC_ARRAY(x, i) (OT__NAME(x), v_OT->MallocArray(this->x, i, false))
/**
* Within OT_DEF, declare a pointer to an object that might be NULL.
*/
#define OT_PTR_NULLABLE(x) (OT__NAME(x), v_OT->Ptr(this->x, true))
/**
* Within OT_DEF, declare a pointer to an array that might be NULL.
*/
#define OT_ARRAY_NULLABLE(x, i) (OT__NAME(x), v_OT->Array(this->x, i, true))
/**
* Within OT_DEF, declare a pointer to a malloced array that might be NULL.
*/
#define OT_MALLOC_ARRAY_NULLABLE(x, i) (OT__NAME(x), v_OT->MallocArray(this->x, i, true))
/**
* Define the object traversal for this object.
*
* Example:
* @code
* class MyTree {
* private:
* int value;
* MyTree *left;
* MyTree *right;
* int num_extra_data;
* Data *extra_data_array;
*
* OT_DEF(MyTree) {
* OT_MY_OBJECT(value);
* OT_PTR_NULLABLE(left);
* OT_PTR_NULLABLE(right);
* OT_MY_OBJECT(num_extra_data);
* OT_ARRAY(extra_data_array, num_extra_data);
* }
* };
* ... rest of class definition ...
* @endcode
*
* The OT_DEF declares its own members, and its pointers. Notice that
* <code>OT_MY_OBJECT(num_extra_data)</code> must come before the subsequent
* line that uses num_extra_data as an array length. If deserialization is
* occuring, each <code>OT_...</code> call is actually deserializing each
* member, so num_extra_data is uninitialized until <code>OT_MY_OBJECT</code>
* is called on it.
*
* Fine-point: If you have an array of pointers, you are pretty much doomed
* to declare the array of pointers and iterate over the array yourself for
* each pointer, treating each element of the array as a separate pointer.
*
* @see OT_MY_OBJECT, OT_MY_ARRAY, OT_PTR, OT_ARRAY, OT_MALLOC_ARRAY,
* OT_PTR_NULLABLE, OT_ARRAY_NULLABLE, OT_MALLOC_ARRAY_NULLABLE.
*/
#define OT_DEF(AClass) \
public: \
template<typename Visitor> \
friend void TraverseObject(AClass *obj_OT, Visitor *v_OT) { \
obj_OT->TraverseObject__OT_(v_OT); \
} \
private: \
template<typename Visitor> \
void TraverseObject__OT_(Visitor *v_OT)
// Re-think how this is supposed to work.
// /**
// * Create an automatically-generated print method for your class.
// */
// #define OT_GENERATE_PRINT(AClass)
// public:
// template<>
// friend void Print(const AClass& obj, FILE *stream) {
// OTPrint(obj, stream);
// }
// TODO: Automatically generate copy constructors and the like
/**
* Like OT_DEF, but automatically generates as many standard methods as
* possible.
*/
#define OT_FULL(AClass) \
OT_GENERATE_PRINT(AClass) \
OT_DEF(AClass)
/**
* Specify a clean-up step to run after deserialization, for instance, to
* populate transient fields.
*
* An example is ArrayList - it has both a length and capacity. The capacity
* need not be stored, but upon deserialization, the capacity must be
* initialized to a valid value, such as the length.
*/
#define OT_FIX(AClass) \
public: \
friend void TraverseObjectPostprocess(AClass *x) { \
x->TraverseObjectPostprocess__OT_(); \
} \
private: \
void TraverseObjectPostprocess__OT_()
// The object-tree-visitor interface.
// class OTBlankVisitor {
// public:
// /** visits an object with no OT implementation */
// template<typename T> void Primitive(T& x);
// /** visits an internal object */
// template<typename T> void MyObject(T& x);
// /** visits an array */
// template<typename T> void MyArray(T* x, index_t i);
// /** visits an object pointed to, allocated with new */
// template<typename T> void Ptr(T*& x, bool nullable);
// /** visits an array pointed to, allocated with new[] */
// template<typename T> void Array(T*& x, index_t i, bool nullable);
// /** visits an array pointed to, allocated with malloc */
// template<typename T> void MallocArray(T*& x, index_t i, bool nullable);
// };
/**
* Perform object-tree traversal on a single object with a given object-tree
* visitor.
*
* The visitor can perform pretty much any function it wants with the
* contents of each data type. It can print, serialize, deserialize,
* pointer-freeze, etc.
*/
template<typename T, typename Visitor>
inline void TraverseObject(T* x, Visitor* v) {
v->Primitive(*x);
}
/**
* Postprocess function for making copies, to fix anything that may be
* inaccurate from a plain copy.
*
* You will probably never need to implement this. This exists
* mainly so that lazy-rezing data structures (i.e. ArrayList) can serialize
* themselves as their trimmed size -- the TraverseObject function neglects
* saving the capacity, and fills in the capacity upon deserialization.
* Note this should NOT dereference any pointers within the object, just
* update things like flags.
*/
template<typename T>
inline void TraverseObjectPostprocess(T* x) {
}
/**
* Traverses an array with a particular visitor.
*
* This is a convenience method that just calls TraverseObject on each
* element.
*/
template<typename T, typename Visitor>
inline void TraverseArray(T* x, index_t n_elems, Visitor *v) {
for (index_t i = 0; i < n_elems; i++) {
TraverseObject(&x[i], v);
}
}
/**
* Private namespace for object-traversal utilities.
*/
namespace ot_private {
// TODO: Space-conservatory serialization and deserialization
// (Currently only freezing/thawing is supported)
// These have to be hoisted out of the class.
// Apparently explicit specialization for templates cannot be done in class
// scope.
/** Visits an object with no OT implementation. */
/* template<typename DefaultPrinter, typename Printer, typename T>
void OTPrinter_Primitive(
const char *name, T& x, Printer* printer) {
DefaultPrinter::Print(name, x, printer);
}
template<typename DefaultPrinter, typename Printer>
inline void OTPrinter_Primitive(
const char *name, const char* x, Printer* printer) {
printer->Write("%s : string = %s", name, x);
}
template<typename DefaultPrinter, typename Printer>
inline void OTPrinter_Primitive(
const char *name, char x, Printer* printer) {
if (isprint(x)) {
printer->Write("%s : char = %d '%c'", name, x, x);
} else {
printer->Write("%s : char = %d", name, x);
}
}
template<typename DefaultPrinter, typename Printer>
inline void OTPrinter_Primitive(
const char *name, short x, Printer* printer) {
printer->Write("%s : short = %d", name, x);
}
template<typename DefaultPrinter, typename Printer>
inline void OTPrinter_Primitive(
const char *name, int x, Printer* printer) {
printer->Write("%s : int = %d", name, x);
}
template<typename DefaultPrinter, typename Printer>
inline void OTPrinter_Primitive(
const char *name, long x, Printer* printer) {
printer->Write("%s : long = %ld", name, x);
}
template<typename DefaultPrinter, typename Printer>
inline void OTPrinter_Primitive(
const char *name, unsigned char x, Printer* printer) {
printer->Write("%s : uchar = %u", name, x);
}
template<typename DefaultPrinter, typename Printer>
inline void OTPrinter_Primitive(
const char *name, unsigned short x, Printer* printer) {
printer->Write("%s : ushort = %u", name, x);
}
template<typename DefaultPrinter, typename Printer>
inline void OTPrinter_Primitive(
const char *name, unsigned int x, Printer* printer) {
printer->Write("%s : uint = %u", name, x);
}
template<typename DefaultPrinter, typename Printer>
inline void OTPrinter_Primitive(
const char *name, unsigned long x, Printer* printer) {
printer->Write("%s : ulong = %lu", name, x);
}
template<typename DefaultPrinter, typename Printer>
inline void OTPrinter_Primitive(
const char *name, float x, Printer* printer) {
printer->Write("%s : float = %f", name, x);
}
template<typename DefaultPrinter, typename Printer>
inline void OTPrinter_Primitive(
const char *name, double x, Printer* printer) {
printer->Write("%s : double = %f", name, x);
}
*/
template<typename DefaultPrinter, typename Printer, typename T>
struct OTPrinter_Primitive {
static void Print(const char *name, T& x, Printer* printer) {
DefaultPrinter::Print(name, x, printer);
}
};
/* macro for use within this file */
#define OTPRINTER__SPECIAL(T, format_str) \
template<typename DefaultPrinter, typename Printer> \
struct OTPrinter_Primitive<DefaultPrinter, Printer, T> { \
static void Print(const char *name, T x, Printer *printer) { \
printer->Write("%s : "format_str, name, x); \
} \
};
OTPRINTER__SPECIAL(const char*, "string = %s");
OTPRINTER__SPECIAL(char, "char = %d");
OTPRINTER__SPECIAL(short, "short = %d");
OTPRINTER__SPECIAL(int, "int = %d");
OTPRINTER__SPECIAL(long, "long = %ld");
OTPRINTER__SPECIAL(unsigned char, "char = %u");
OTPRINTER__SPECIAL(unsigned short, "short = %u");
OTPRINTER__SPECIAL(unsigned int, "int = %u");
OTPRINTER__SPECIAL(unsigned long, "long = %lu");
OTPRINTER__SPECIAL(float, "float = %f");
OTPRINTER__SPECIAL(double, "double = %f");
/**
* Takes an OT-compatible object and prints it to screen.
*/
class OTPrinter {
private:
FILE *stream_;
int indent_amount_;
const char *name_;
private:
template<typename T>
struct DefaultPrimitivePrinter {
static void Print(const char *name, const T& x, OTPrinter *printer) {
printer->ShowIndents();
for (size_t i = 0; i < sizeof(T); i++) {
fprintf(printer->stream(), " %02X",
reinterpret_cast<const unsigned char*>(&x)[i]);
}
fprintf(printer->stream(), "\n");
}
};
template<typename T>
struct DefaultObjectPrinter {
static void Print(const char *name, T& x, OTPrinter *printer) {
printer->Write("%s : %s {", name, typeid(T).name());
printer->Indent(2);
TraverseObject(&x, printer);
printer->Indent(-2);
printer->Write("}");
}
};
public:
template<typename T>
void InitBegin(const T& x, FILE *stream_in) {
stream_ = stream_in;
indent_amount_ = 0;
TraverseObject(const_cast<T*>(&x), this);
}
/** Stores the name of the object going to come in. */
void Name(const char *s) {
name_ = s;
}
template<typename T> void Primitive(T& x) {
OTPrinter_Primitive< DefaultPrimitivePrinter<T>, OTPrinter, T >
::Print(name_, x, this);
}
template<typename T> void Object(T* obj, bool nullable,
const char *label) {
if (nullable && !obj) {
Write("%s : %s %s = NULL", name_, label, typeid(T).name());
} else {
OTPrinter_Primitive< DefaultObjectPrinter<T>, OTPrinter, T >
::Print(name_, *obj, this);
}
}
template<typename T> void Array(T* array, index_t len,
bool nullable) {
if (nullable && !array) {
Write("%s : %s[] = NULL", name_, typeid(T).name());
} else {
Write("%s : %s[%"LI"d] = {", name_, typeid(T).name(), len);
Indent(2);
for (index_t i = 0; i < len; i++) {
Write("element %"LI"d {", i);
Indent(2);
name_ = "(array element)";
TraverseObject(&array[i], this);
Indent(-2);
Write("}");
}
Indent(-2);
Write("}");
}
}
/** Visits an internal object. */
template<typename T> void MyObject(T& x) {
// Recurse in case this sub-object has pointers
Object(&x, false, "embedded");
}
/** Visits an array. */
template<typename T> void MyArray(T* x, index_t len) {
// Recurse in case any of these objects have pointers
Array(x, len, false);
}
/**
* Visits an object pointed to, allocated with new.
*
* This allocates space within the block for the pointer, copies the
* data pointed to, and recurses on the data pointed to.
*/
template<typename T> void Ptr(T*& source_region, bool nullable) {
Object(source_region, nullable, "pointer-to");
}
/** Visits an array pointed to, allocated with malloc */
template<typename T> void MallocArray(T*& source_region, index_t len,
bool nullable) {
Array(source_region, len, nullable);
}
public:
void Indent(int delta) {
indent_amount_ += delta;
}
void Write(const char *format, ...);
void ShowIndents();
FILE *stream() const {
return stream_;
}
};
/**
* Takes an OT-compatible object and saves a linear copy in a block of
* memory.
*
* This is analogous to serialization but distinct. Serialization does
* not allocate space for transient fields such as pointers. However, this
* dumps every object in its entirety, with the hope that bringing the
* object "back to life" is very quick. When stored, each pointer is
* normalized to zero, and the object can be brought back to life by just
* renormalizing all the pointers.
*
* The code here is far more complex than I expected it to be -- please
* read the comments!
*
* ANY MODIFICATIONS TO THIS MUST ALSO BE MADE TO THE SIZE CALCULATOR!
*/
class OTPointerFreezer {
private:
/** The block of memory to freeze into. */
char *block_;
/** The current position within the block. */
ptrdiff_t pos_;
/**
* For updating pointers with normalized pointers, this is the difference
* between the destination and source regions for the *current* object
* being considered.
*/
ptrdiff_t freeze_offset_;
public:
template<typename T>
void InitBegin(const T& x, char *block_in) {
block_ = block_in;
pos_ = sizeof(T);
freeze_offset_ = mem::PointerDiff(block_, &x);
mem::Copy(reinterpret_cast<T*>(block_), &x);
// we must cast away const due to TraverseObject's limitations
TraverseObject(const_cast<T*>(&x), this);
}
size_t size() const {
return stride_align_max(pos_);
}
/** Receives the nanme of the upcoming object -- we ignore this. */
void Name(const char *s) {}
/** Visits an object with no OT implementation. */
template<typename T> void Primitive(T& x) {
// Primitives can be bit-copied
}
/** Visits an internal object. */
template<typename T> void MyObject(T& x) {
// Recurse in case this sub-object has pointers
TraverseObject(&x, this);
}
/** Visits an array. */
template<typename T> void MyArray(T* x, index_t len) {
// Recurse in case any of these objects have pointers
TraverseArray(x, len, this);
}
/**
* Visits an object pointed to, allocated with new.
*
* This allocates space within the block for the pointer, copies the
* data pointed to, and recurses on the data pointed to.
*/
template<typename T> void Ptr(T*& source_region, bool nullable);
/**
* Visits an array pointed to, allocated with new[].
*
* This allocates space within the block for the array, copies the
* data pointed to, and recurses on the array's elements.
*/
template<typename T> void Array(T*& source_region, index_t len,
bool nullable);
/** Visits an array pointed to, allocated with malloc */
template<typename T> void MallocArray(T*& source_region, index_t len,
bool nullable) {
Array(source_region, len, nullable);
}
private:
template <typename T>
/**
* Gets a pointer to the pointer in the destination region that needs
* to be updated. A picture might help.
*
* @param source_region_ptr the pointer to the original pointer, in
* its original location within the larger structure, used with
* pointer arithmetic for updating the resulting pointers
*/
T** DestinationEquivalentPointer_(T** source_region_ptr) {
return mem::PointerAdd(source_region_ptr, freeze_offset_);
}
/**
* Aligns the current position to the given stride, and returns a
* normalized-to-zero pointer for its data, fixing the result pointer
* too.
*
* In reality, this is just a couple assembly instructions.
*
* @param source_region_ptr the pointer to the original pointer, in
* its original location within the larger structure, used with
* pointer arithmetic for updating the resulting pointers
*/
template <typename T>
T* TranslateAndFixPointer_(T** source_region_ptr) {
// Make sure we are aligned to the proper alignment for the data
pos_ = stride_align(pos_, T);
// Find the pointer in the frozen block by adding the "freeze offset"
// This offset basically says "Given some memory within the live object
// that is being frozen, find the corresponding memory within the
// object that is being frozen".
T** pointer_to_fix = DestinationEquivalentPointer_(source_region_ptr);
// We already copied the source region to the destination we are
// considering, so the value of these two pointers should be equal.
DEBUG_ASSERT_MSG(*pointer_to_fix == *source_region_ptr,
"%p != %p", *pointer_to_fix, *source_region_ptr);
// Now, we normalize the pointer such that zero is the beginning of the
// chynk of memory.
*pointer_to_fix = reinterpret_cast<T*>(pos_);
// Return the pointer within the block where future accesses should occur.
return reinterpret_cast<T*>(block_ + pos_);
}
};
template<typename T> void OTPointerFreezer::Ptr(
T*& source_region, bool nullable) {
if (nullable && unlikely(source_region == NULL)) {
*DestinationEquivalentPointer_(&source_region) = NULL;
} else {
// Get the pointer we will write into, and fix our internal pointer
T* dest = TranslateAndFixPointer_(&source_region);
// Copy the object and progress
pos_ += sizeof(T);
mem::Copy(dest, source_region);
// Save our old freeze offset
size_t freeze_offset_tmp = freeze_offset_;
// Calculate new freeze offset as the distance between the source and
// destination memory regions.
freeze_offset_ = mem::PointerDiff(dest, source_region);
// Recurse on the object.
TraverseObject(source_region, this);
TraverseObjectPostprocess(dest);
// Revert to the old freeze offset.
freeze_offset_ = freeze_offset_tmp;
}
}
template<typename T> void OTPointerFreezer::Array(
T*& source_region, index_t len, bool nullable) {
if (nullable && unlikely(source_region == NULL)) {
*DestinationEquivalentPointer_(&source_region) = NULL;
} else {
// Get the pointer we will write into, and fix our internal pointer
T* dest = TranslateAndFixPointer_(&source_region);
// Calculate the total size allocated, copy, and progress
size_t size = len * sizeof(T);
pos_ += size;
mem::CopyBytes(dest, source_region, size);
// Save old freeze offset
size_t freeze_offset_tmp = freeze_offset_;
// Calculate new freeze offset
freeze_offset_ = mem::PointerDiff(dest, source_region);
// Recurse over each object
for (index_t i = 0; i < len; i++) {
TraverseObject(&source_region[i], this);
TraverseObjectPostprocess(&dest[i]);
}
// Restore old freeze offset because we have returned to the old object
freeze_offset_ = freeze_offset_tmp;
}
}
class OTFrozenSizeCalculator {
private:
size_t pos_;
public:
template<typename T>
void InitBegin(const T& obj) {
pos_ = 0;
PretendLayout_<T>(1);
TraverseObject(const_cast<T*>(&obj), this);
}
/**
* Returns the calculated size.
*/
size_t size() const {
return stride_align_max(pos_);
}
/** Receives the nanme of the upcoming object -- we ignore this. */
void Name(const char *s) {}
/** visits an object with no OT implementation */
template<typename T> void Primitive(T& x) {}
/** visits an internal object */
template<typename T> void MyObject(T& x) {
TraverseObject(&x, this);
}
/** visits an array */
template<typename T> void MyArray(T* x, index_t len) {
TraverseArray(x, len, this);
}
/** visits an object pointed to, allocated with new */
template<typename T> void Ptr(T*& x, bool nullable) {
if (!nullable || x != NULL) {
PretendLayout_<T>(1);
TraverseObject(x, this);
}
}
/** visits an array pointed to, allocated with new[] */
template<typename T> void Array(T*& x, index_t len, bool nullable) {
if (!nullable || x != NULL) {
PretendLayout_<T>(len);
TraverseArray(x, len, this);
}
}
/** visits an array pointed to, allocated with malloc */
template<typename T> void MallocArray(T*& x, index_t len, bool nullable) {
Array(x, len, nullable);
}
private:
template<typename T>
void PretendLayout_(index_t count) {
pos_ = (stride_align(pos_, T)) + (sizeof(T) * count);
}
};
// class OTPointerThawer {
// private:
// ptrdiff_t offset_;
//
// public:
// template<typename T>
// T* InitBegin(char *data, ptrdiff_t offset_in) {
// offset_ = offset_in;
// T* dest = reinterpret_cast<T*>(data);
// TraverseObject(dest, this);
// return dest;
// }
//
// template<typename T>
// T* InitBegin(char *data) {
// return InitBegin<T>(data, reinterpret_cast<ptrdiff_t>(data));
// }
//
// /** Receives the nanme of the upcoming object -- we ignore this. */
// void Name(const char *s) {}
//
// /** visits an object with no OT implementation */
// template<typename T> void Primitive(T& x) {}
// /** visits an internal object */
// template<typename T> void MyObject(T& x) {
// TraverseObject(&x, this);
// }
// /** visits an array */
// template<typename T> void MyArray(T* x, index_t len) {
// TraverseArray(x, len, this);
// }
// /** visits an object pointed to, allocated with new */
// template<typename T> void Ptr(T*& x, bool nullable) {
// if (!nullable || x != NULL) {
// x = mem::PointerAdd(x, offset_);
// TraverseObject(x, this);
// }
// }
// /** visits an array pointed to, allocated with new[] */
// template<typename T> void Array(T*& x, index_t len, bool nullable) {
// if (!nullable || x != NULL) {
// x = mem::PointerAdd(x, offset_);
// TraverseArray(x, len, this);
// }
// }
// /** visits an array pointed to, allocated with malloc */
// template<typename T> void MallocArray(T*& x, index_t len, bool nullable) {
// Array(x, len, nullable);
// }
// };
class OTPointerRelocator {
private:
ptrdiff_t pre_offset_;
ptrdiff_t post_offset_;
public:
/**
* Fixes pointers.
*
* @param pre_offset_in the offset between where the pointers are
* currently pointing, and where they would need to point in
* order to recurse on the data structure (no modifications made)
* @param post_offset_in the offset between where the pointers are
* currently pointing, and the new address space they are relocated
* to
* @param dest the object to recurse on
*/
template<typename T>
T* InitBegin(ptrdiff_t pre_offset_in, ptrdiff_t post_offset_in, T *dest) {
pre_offset_ = pre_offset_in;
post_offset_ = post_offset_in;
TraverseObject(dest, this);
return dest;
}
/** Receives the nanme of the upcoming object -- we ignore this. */
void Name(const char *s) {}
/** visits an object with no OT implementation */
template<typename T> void Primitive(T& x) {}
/** visits an internal object */
template<typename T> void MyObject(T& x) {
TraverseObject(&x, this);
}
/** visits an array */
template<typename T> void MyArray(T* x, index_t len) {
TraverseArray(x, len, this);
}
/** visits an object pointed to, allocated with new */
template<typename T> void Ptr(T*& x, bool nullable) {
if (!nullable || x != NULL) {
TraverseObject(mem::PointerAdd(x, pre_offset_), this);
x = mem::PointerAdd(x, post_offset_);
}
}
/** visits an array pointed to, allocated with new[] */
template<typename T> void Array(T*& x, index_t len, bool nullable) {
if (!nullable || x != NULL) {
TraverseArray(mem::PointerAdd(x, pre_offset_), len, this);
x = mem::PointerAdd(x, post_offset_);
}
}
/** visits an array pointed to, allocated with malloc */
template<typename T> void MallocArray(T*& x, index_t len, bool nullable) {
Array(x, len, nullable);
}
};
}; // namespace ot_private
namespace ot {
template<typename T>
void Print(const T& object, FILE *stream = stderr) {
ot_private::OTPrinter printer;
printer.InitBegin(object, stream);
}
/**
* Finds the number of bytes required to freeze an object.
*/
template<typename T>
size_t PointerFrozenSize(const T& obj) {
ot_private::OTFrozenSizeCalculator calc;
calc.InitBegin(obj);
return calc.size();
}
/**
* Makes a copy of an object, freezing it for the first time.
*/
template<typename T>
void PointerFreeze(const T& live_object, char *block) {
ot_private::OTPointerFreezer freezer;
freezer.InitBegin(live_object, block);
DEBUG_SAME_INT(freezer.size(), ot::PointerFrozenSize(live_object));
}
/**
* Takes an object that is laid out serially, and adjusts all its pointers
* so that they are normalized to zero.
*/
template<typename T>
void PointerRefreeze(T* obj) {
ot_private::OTPointerRelocator fixer;
fixer.InitBegin<T>(
0, -mem::PointerAbsoluteAddress(obj),
reinterpret_cast<T*>(obj));
}
/**
* Takes an object that is laid out serially, and adjusts all its pointers
* so that they are normalized to zero.
*
* This assumes that "dest" is an object that is laid out serially, but
* all its pointers are as if it had been copied from src. This is used
* for reading from an existing cache -- the pointers are fixed in a
* temporary buffer rather than in the cache, so that other threads do not
* experience any negative side effects.
*/
template<typename T>
void PointerRefreeze(const T* src, char* dest) {
ot_private::OTPointerRelocator fixer;
fixer.InitBegin<T>(
mem::PointerDiff(dest, src), -mem::PointerAbsoluteAddress(src),
reinterpret_cast<T*>(dest));
}
/**
* Takes an object that is laid out serially with all its pointers
* normalized to zero, and makes all the pointers live again.
*/
template<typename T>
T* PointerThaw(char *block) {
ot_private::OTPointerRelocator fixer;
return fixer.InitBegin<T>(
mem::PointerAbsoluteAddress(block),
mem::PointerAbsoluteAddress(block),
reinterpret_cast<T*>(block));
}
/**
* Relocates an object from a previous location to a new location.
*
* Call this to fix pointers after swapping or memcopying an object.
*/
template<typename T>
void PointerRelocate(const char *old_location, char *new_location) {
ot_private::OTPointerRelocator fixer;
fixer.InitBegin<T>(
mem::PointerDiff(new_location, old_location),
mem::PointerDiff(new_location, old_location),
reinterpret_cast<T*>(new_location));
}
};
#endif