Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Voron’s Implementation: Managed Memory Mapped Database–Getting to C Memory Model

DZone's Guide to

Voron’s Implementation: Managed Memory Mapped Database–Getting to C Memory Model

· Database Zone
Free Resource

Learn how to create flexible schemas in a relational database using SQL for JSON.

In my previous post I started to talk about the architecture of Naver. But this is actually still too early to tell, since before anything else, I want to create the low level interface for working with pages. And even before that, I had to decide how to actually represent a page in C#. In LMDB, this is easy because you can just access the memory using a pointer, and work with memory in this fashion is really natural in C. But in C#, that is much harder. It took me some trial and error, but I realize that I was trying to write C code in C#, and that isn’t going to work. Instead, I have to write native C#, which is similar, but different. In C, I can just pass around a pointer, and start doing evil things to it at will. In C#, there are a lot of road blocks along the way that prevent you from doing that.

So I ended up with this:

[StructLayout(LayoutKind.Explicit, Pack = 1)]

 public struct PageHeader

 {

     [FieldOffset(0)]

     public int PageNumber;

     [FieldOffset(4)]

     public PageFlags Flags;

  

     [FieldOffset(5)]

     public ushort Lower;

     [FieldOffset(7)]

     public ushort Upper;

  

     [FieldOffset(5)]

     public int NumberOfPages;

 }

This is the memory layout that I want. But, there is no way in C# to say, allocate this structure at this location. Luckily, I found a workaround.

public unsafe class Page

 {

     private readonly byte* _base;

     private readonly PageHeader* _header;

  

     public Page(byte* b)

     {

         _base = b;

         _header = (PageHeader*)b;

     }

  

     public int PageNumber { get { return _header->PageNumber; } set { _header->PageNumber = value; } }

What I can do, I can have a Page class that manage this for me. This means that I just give the class a pointer, and it can treat this either as a PageHeader* or as a byte array. This means that I also get to do cool tricks like having an array backed by memory directly in the code:

public ushort* KeysOffsets

 {

     get { return (ushort*)(_base + Constants.PageHeaderSize); }

 }

The Page class have a lot of methods relating to managing the page itself, and it should abstract away all the pesky details of actually working with memory directly.

So, this isn’t a really informative post, I fear. But it did take me a few hours to come up with the approach that I wanted. I kept trying to find ways to embed addresses inside the PageHeader, until I realized that I can just hold that externally. Note that all the important state about the Page is actually stored in memory outside the Page class, so that is shared globally, but you can have two Page instances that point to the same place. And that is where I decided to put local state. Things like where we are currently positioned in the page, for example.



Create flexible schemas using dynamic columns for semi-structured data. Learn how.

Topics:

Published at DZone with permission of Oren Eini, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}