--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * sb_map.c
+ * Superblock allocator page-mapping infrastructure.
+ *
+ * The superblock allocator does not store metadata with each chunk, and
+ * therefore needs a way to find the metadata given only the pointer
+ * address. The first step is to translate the pointer address to a
+ * an offset relative to some base address, from which a page number
+ * can be calculated. Then, this module is reponsible for mapping the
+ * page number to an offset with the chunk where the associated span
+ * object is stored. We do this in the simplest possible way: one big
+ * array.
+ *
+ * Span metadata is stored within the same chunk of memory as the span
+ * itself. Therefore, we can assume that the offset is less than 4GB
+ * whenever we're managing less than 4GB of pages, and use 4 byte
+ * offsets. When we're managing more than 4GB of pages, we use 8 byte
+ * offsets. (This could probably be optimized; for example, we could use
+ * 6 byte offsets for allocation sizes up to 256TB; also, if we assumed
+ * that the span object must itself be 2, 4, or 8 byte aligned, we could
+ * extend the cutoff point for offsets of any given length by a similar
+ * multiple. It's not clear that the extra math would be worthwhile.)
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/utils/sb_map.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "storage/shmem.h"
+#include "utils/freepage.h"
+#include "utils/sb_map.h"
+
+const uint64 maxpages_4b = UINT64CONST(0x100000000) / FPM_PAGE_SIZE;
+
+struct sb_map
+{
+ Size npages;
+};
+
+/* Map layout for segments less than 4GB. */
+typedef struct sb_map32
+{
+ sb_map hdr;
+ uint32 map[FLEXIBLE_ARRAY_MEMBER];
+} sb_map32;
+
+/* Map layout for segments less than 8GB. */
+typedef struct sb_map64
+{
+ sb_map hdr;
+ uint64 map[FLEXIBLE_ARRAY_MEMBER];
+} sb_map64;
+
+/*
+ * Compute the amount of space required for an sb_map covering a given
+ * number of pages. Note we assume that the maximum offset we'll be asked
+ * to store is governed by that number of pages also.
+ */
+Size
+sb_map_size(Size npages)
+{
+ Size map_bytes;
+
+ if (npages < maxpages_4b)
+ map_bytes = add_size(offsetof(sb_map32, map),
+ mul_size(npages, sizeof(uint32)));
+ else
+ map_bytes = add_size(offsetof(sb_map64, map),
+ mul_size(npages, sizeof(uint64)));
+
+ return map_bytes;
+}
+
+/*
+ * Initialize an sb_map. Storage is provided by the caller. Note that we
+ * don't zero the array; the caller shouldn't try to get a value that hasn't
+ * been set.
+ */
+void
+sb_map_initialize(sb_map *m, Size npages)
+{
+ m->npages = npages;
+}
+
+/*
+ * Store a value into an sb_map.
+ */
+void
+sb_map_set(sb_map *m, Size pageno, Size offset)
+{
+ Assert(pageno < m->npages);
+ Assert(offset / FPM_PAGE_SIZE < m->npages);
+
+ if (m->npages < maxpages_4b)
+ ((sb_map32 *) m)->map[pageno] = (uint32) offset;
+ else
+ ((sb_map64 *) m)->map[pageno] = (uint32) offset;
+}
+
+/*
+ * Get a value from an sb_map. Getting a value not previously stored will
+ * produce an undefined result, so don't do that.
+ */
+Size
+sb_map_get(sb_map *m, Size pageno)
+{
+ Assert(pageno < m->npages);
+
+ if (m->npages < maxpages_4b)
+ return ((sb_map32 *) m)->map[pageno];
+ else
+ return ((sb_map64 *) m)->map[pageno];
+}
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * sb_map.h
+ * Superblock allocator page-mapping infrastructure.
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/utils/sb_map.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef SB_MAP_H
+#define SB_MAP_H
+
+typedef struct sb_map sb_map;
+
+extern Size sb_map_size(Size npages);
+extern void sb_map_initialize(sb_map *, Size npages);
+extern void sb_map_set(sb_map *, Size pageno, Size offset);
+extern Size sb_map_get(sb_map *, Size pageno);
+
+#endif /* SB_MAP_H */