--- /dev/null
+/*
+ * Copyright (c) 2000-2006,2011-2012,2014 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+
+//
+// walkers - facilities for traversing and manipulating recursive data structures
+//
+// Very briefly, this facility allows for deep traversals of (potentially) recursive
+// data structures through templated structure "walkers." Standard operations include
+// deep copying to a contiguous memory buffer, size calculation, deep freeing, reconstitution
+// after relocation (e.g. via IPC), and others. You can add other operations (e.g. scattered deep
+// copy, debug dumping, etc.) by defining operations classes and applying them to the
+// existing walkers. You can also extend the reach of the facility to new data structures
+// by writing appropriate walker functions for them.
+//
+// NOTE: We no longer have a default walker for flat structures. You must define
+// a walk(operate, foo * &) function for every data type encountered during a walk
+// or you will get compile-time errors.
+//
+// For more detailed rules and regulations, see the accompanying documentation.
+//
+#ifndef _H_WALKERS
+#define _H_WALKERS
+
+#include <security_utilities/alloc.h>
+#include <security_utilities/memstreams.h>
+#include <security_cdsa_utilities/cssmdata.h>
+#include <security_utilities/debugging.h>
+#include <set>
+
+
+namespace Security {
+namespace DataWalkers {
+
+#define WALKERDEBUG 0
+
+
+#if WALKERDEBUG
+# define DEBUGWALK(who) secdebug("walkers", "walk " who " %s@%p (%ld)", \
+ Debug::typeName(addr).c_str(), addr, size)
+#else
+# define DEBUGWALK(who) /* nothing */
+#endif
+
+
+//
+// SizeWalker simply walks a structure and calculates how many bytes
+// CopyWalker would use to make a flat copy. This is naturally at least
+// the sum of all relevant sizes, but can be more due to alignment and
+// counting overhead.
+//
+class SizeWalker : public LowLevelMemoryUtilities::Writer::Counter {
+public:
+ template <class T>
+ void operator () (T &obj, size_t size = sizeof(T)) { }
+
+ template <class T>
+ void operator () (T *addr, size_t size = sizeof(T))
+ { DEBUGWALK("size"); LowLevelMemoryUtilities::Writer::Counter::insert(size); }
+
+ void blob(void *addr, size_t size)
+ { (*this)(addr, size); }
+
+ void reserve(size_t space)
+ { LowLevelMemoryUtilities::Writer::Counter::insert(space); }
+
+ static const bool needsRelinking = false;
+ static const bool needsSize = true;
+};
+
+
+//
+// CopyWalker makes a deep, flat copy of a structure. The result will work
+// just like the original (with all elements recursively copied), except that
+// it occupies contiguous memory.
+//
+class CopyWalker : public LowLevelMemoryUtilities::Writer {
+public:
+ CopyWalker() { }
+ CopyWalker(void *base) : LowLevelMemoryUtilities::Writer(base) { }
+
+public:
+ template <class T>
+ void operator () (T &obj, size_t size = sizeof(T))
+ { }
+
+ template <class T>
+ void operator () (T * &addr, size_t size = sizeof(T))
+ {
+ DEBUGWALK("copy");
+ if (addr)
+ addr = reinterpret_cast<T *>(LowLevelMemoryUtilities::Writer::operator () (addr, size));
+ }
+
+ template <class T>
+ void blob(T * &addr, size_t size)
+ { (*this)(addr, size); }
+
+ static const bool needsRelinking = true;
+ static const bool needsSize = true;
+};
+
+
+//
+// Walk a structure and apply a constant linear shift to all pointers
+// encountered. This is useful when a structure and its deep components
+// have been linearly shifted by something (say, an IPC transit).
+//
+class ReconstituteWalker {
+public:
+ ReconstituteWalker(off_t offset) : mOffset(offset) { }
+ ReconstituteWalker(void *ptr, void *base)
+ : mOffset(LowLevelMemoryUtilities::difference(ptr, base)) { }
+
+ template <class T>
+ void operator () (T &obj, size_t size = sizeof(T))
+ { }
+
+ template <class T>
+ void operator () (T * &addr, size_t size = 0)
+ {
+ DEBUGWALK("reconstitute");
+ if (addr)
+ addr = LowLevelMemoryUtilities::increment<T>(addr, (ptrdiff_t)mOffset);
+ }
+
+ template <class T>
+ void blob(T * &addr, size_t size)
+ { (*this)(addr, size); }
+
+ static const bool needsRelinking = true;
+ static const bool needsSize = false;
+
+private:
+ off_t mOffset;
+};
+
+
+//
+// Make an element-by-element copy of a structure. Each pointer followed
+// uses a separate allocation for its pointed-to storage.
+//
+class ChunkCopyWalker {
+public:
+ ChunkCopyWalker(Allocator &alloc = Allocator::standard()) : allocator(alloc) { }
+
+ Allocator &allocator;
+
+ template <class T>
+ void operator () (T &obj, size_t size = sizeof(T))
+ { }
+
+ template <class T>
+ void operator () (T * &addr, size_t size = sizeof(T))
+ {
+ DEBUGWALK("chunkcopy");
+#if BUG_GCC
+ T *copy = reinterpret_cast<T *>(allocator.malloc(size));
+#else
+ T *copy = allocator.malloc<T>(size);
+#endif
+ memcpy(copy, addr, size);
+ addr = copy;
+ }
+
+ template <class T>
+ void blob(T * &addr, size_t size)
+ { (*this)(addr, size); }
+
+ static const bool needsRelinking = true;
+ static const bool needsSize = true;
+};
+
+
+//
+// Walk a structure and call an Allocator to separate free each node.
+// This is safe for non-trees (i.e. shared subsidiary nodes); such will
+// only be freed once.
+//
+class ChunkFreeWalker {
+public:
+ ChunkFreeWalker(Allocator &alloc = Allocator::standard()) : allocator(alloc) { }
+
+ Allocator &allocator;
+
+ template <class T>
+ void operator () (T &obj, size_t size = 0)
+ { }
+
+ template <class T>
+ void operator () (T *addr, size_t size = 0)
+ {
+ DEBUGWALK("chunkfree");
+ freeSet.insert(addr);
+ }
+
+ void blob(void *addr, size_t size)
+ { (*this)(addr, 0); }
+
+ void free();
+ ~ChunkFreeWalker() { free(); }
+
+ static const bool needsRelinking = false;
+ static const bool needsSize = false;
+
+private:
+ std::set<void *> freeSet;
+};
+
+
+//
+// Stand-alone operations for a single structure web.
+// These simply create, use, and discard their operator objects internally.
+//
+template <class T>
+size_t size(T obj)
+{
+ SizeWalker w;
+ walk(w, obj);
+ return w;
+}
+
+// Special version for const pointer's
+template <class T>
+size_t size(const T *obj)
+{ return size(const_cast<T *>(obj)); }
+
+
+template <class T>
+T *copy(const T *obj, void *addr)
+{
+ if (obj == NULL)
+ return NULL;
+ CopyWalker w(addr);
+ walk(w, const_cast<T * &>(obj));
+ return const_cast<T *>(obj);
+}
+
+template <class T>
+T *copy(const T *obj, Allocator &alloc, size_t size)
+{
+ if (obj == NULL)
+ return NULL;
+ return copy(obj, alloc.malloc(size));
+}
+
+template <class T>
+T *copy(const T *obj, Allocator &alloc = Allocator::standard())
+{
+ return obj ? copy(obj, alloc, size(obj)) : NULL;
+}
+
+
+template <class T>
+void relocate(T *obj, T *base)
+{
+ if (obj) {
+ ReconstituteWalker w(LowLevelMemoryUtilities::difference(obj, base));
+ walk(w, base);
+ }
+}
+
+
+//
+// chunkCopy and chunkFree can take pointer and non-pointer arguments.
+// Don't try to declare the T arguments const (overload resolution will
+// mess you over if you try). Just take const and nonconst Ts and take
+// the const away internally.
+//
+template <class T>
+typename Nonconst<T>::Type *chunkCopy(T *obj, Allocator &alloc = Allocator::standard())
+{
+ if (obj) {
+ ChunkCopyWalker w(alloc);
+ return walk(w, unconst_ref_cast<T *>(obj));
+ } else
+ return NULL;
+}
+
+template <class T>
+T chunkCopy(T obj, Allocator &alloc = Allocator::standard())
+{
+ ChunkCopyWalker w(alloc);
+ walk(w, obj);
+ return obj;
+}
+
+template <class T>
+void chunkFree(T *obj, Allocator &alloc = Allocator::standard())
+{
+ if (obj) {
+ ChunkFreeWalker w(alloc);
+ walk(w, unconst_ref_cast<T *>(obj));
+ }
+}
+
+template <class T>
+void chunkFree(const T &obj, Allocator &alloc = Allocator::standard())
+{
+ ChunkFreeWalker w(alloc);
+ walk(w, obj);
+}
+
+
+//
+// Copier combines SizeWalker and CopyWalker into one operational package.
+// this is useful if you need both the copy and its size (and don't want
+// to re-run size()). Copier (like copy()) only applies to one object.
+//
+template <class T>
+class Copier {
+public:
+ Copier(const T *obj, Allocator &alloc = Allocator::standard()) : allocator(alloc)
+ {
+ if (obj == NULL) {
+ mValue = NULL;
+ mLength = 0;
+ } else {
+ mLength = size(const_cast<T *>(obj));
+#if BUG_GCC
+ mValue = reinterpret_cast<T *>(alloc.malloc(mLength));
+#else
+ mValue = alloc.malloc<T>(mLength);
+#endif
+ mValue = copy(obj, mValue);
+ }
+ }
+
+ Copier(const T *obj, uint32 count, Allocator &alloc = Allocator::standard())
+ : allocator(alloc)
+ {
+ if (obj == NULL) {
+ mValue = NULL;
+ mLength = 0;
+ } else {
+ SizeWalker sizer;
+ sizer.reserve(sizeof(T) * count); // initial vector size
+ for (uint32 n = 0; n < count; n++)
+ walk(sizer, const_cast<T &>(obj[n])); // dependent data sizes
+ mLength = sizer;
+#if BUG_GCC
+ mValue = reinterpret_cast<T *>(alloc.malloc(mLength));
+#else
+ mValue = alloc.malloc<T>(mLength);
+#endif
+ CopyWalker copier(LowLevelMemoryUtilities::increment(mValue, sizeof(T) * count));
+ for (uint32 n = 0; n < count; n++) {
+ mValue[n] = obj[n];
+ walk(copier, mValue[n]);
+ }
+ }
+ }
+
+ Allocator &allocator;
+
+ ~Copier() { allocator.free(mValue); }
+
+ T *value() const { return mValue; }
+ operator T *() const { return value(); }
+ size_t length() const { return mLength; }
+
+ T *keep() { T *result = mValue; mValue = NULL; return result; }
+
+private:
+ T *mValue;
+ size_t mLength;
+};
+
+
+} // end namespace DataWalkers
+} // end namespace Security
+
+#endif //_H_WALKERS