Skip to content

Commit

Permalink
Documentation etc
Browse files Browse the repository at this point in the history
  • Loading branch information
mgravell committed Nov 12, 2014
1 parent 74a2291 commit 86f5a74
Show file tree
Hide file tree
Showing 13 changed files with 154 additions and 52 deletions.
4 changes: 2 additions & 2 deletions CapnProto-net.Schema.nuspec
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<package>
<metadata>
<id>CapnProto-net.Schema</id>
<version>0.0.3</version>
<version>0.0.4</version>
<authors>marc.gravell</authors>
<owners>marc.gravell</owners>
<summary>Schema parsing / generation tools for CapnProto-net</summary>
Expand All @@ -16,7 +16,7 @@
<copyright>Marc Gravell, 2014</copyright>

<dependencies>
<dependency id="CapnProto-net" version="0.0.3"/>
<dependency id="CapnProto-net" version="0.0.4"/>
</dependencies>
</metadata>
<files>
Expand Down
2 changes: 1 addition & 1 deletion CapnProto-net.nuspec
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<package>
<metadata>
<id>CapnProto-net</id>
<version>0.0.3</version>
<version>0.0.4</version>
<authors>marc.gravell</authors>
<owners>marc.gravell</owners>
<summary>.NET implementation of the Cap'n Proto data interchange protocol</summary>
Expand Down
10 changes: 5 additions & 5 deletions CapnProto.net.Schema/CSharpStructWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ public override bool NeedsSerializer
get { return false; }
}
public CSharpStructWriter(TextWriter destination, FixedSizeList<Schema.Node> nodes,
string @namespace, string serializer)
: base(destination, nodes, @namespace, serializer)
string @namespace)
: base(destination, nodes, @namespace, "not used")
{ }
private int indentationLevel;

Expand Down Expand Up @@ -250,7 +250,7 @@ void WriteListImpl(Schema.Field field)

private static void CascadePointers(CodeWriter writer, Schema.Node node, SortedList<int, List<Tuple<Schema.Node, Schema.Field, Stack<UnionStub>>>> ptrFields, Stack<UnionStub> union)
{
if (node.@struct.fields)
if (node.@struct.fields.IsValid())
{
foreach (var field in node.@struct.fields)
{
Expand Down Expand Up @@ -983,7 +983,7 @@ public override CodeWriter WriteGroup(Schema.Node node, Stack<UnionStub> union)
Indent();
WriteLine().Write("this.").Write(PointerName).Write(" = pointer;");
Outdent();
if (node.@struct.fields)
if (node.@struct.fields.IsValid())
{
foreach (var field in node.@struct.fields.OrderBy(x => x.codeOrder).ThenBy(x => x.name, Text.Comparer))
{
Expand Down Expand Up @@ -1015,7 +1015,7 @@ public override CodeWriter WriteDiscriminant(Schema.Node node, Stack<UnionStub>
if (union.Count != 0) Write("new ");
Write("enum Unions");
Indent();
if (@struct.fields)
if (@struct.fields.IsValid())
{
foreach (var field in @struct.fields)
{
Expand Down
8 changes: 4 additions & 4 deletions CapnProto.net.Schema/CodeWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ protected Schema.Node FindParent(Schema.Node node)
foreach (var pair in map)
{
var nested = pair.Value.nestedNodes;
if (nested)
if (nested.IsValid())
{
foreach (var x in nested)
if (x.id == node.id) return pair.Value;
Expand Down Expand Up @@ -260,7 +260,7 @@ public virtual CodeWriter WriteNestedTypes(Schema.Node node, Stack<UnionStub> un
{
var children = node.nestedNodes;
var seen = new HashSet<ulong>();
if (children)
if (children.IsValid())
{
foreach (var child in children)
{
Expand Down Expand Up @@ -331,7 +331,7 @@ public void WriteStruct(Schema.Node node, Stack<UnionStub> union)
Schema.CodeGeneratorRequest.ComputeSpace(this, node, ref bodyWords, ref pointerWords);
HashSet<ulong> nestedDone = null;

if (fields)
if (fields.IsValid())
{
foreach (var field in fields.OrderBy(x => x.codeOrder).ThenBy(x => x.name, Text.Comparer))
{
Expand Down Expand Up @@ -400,7 +400,7 @@ public void WriteStruct(Schema.Node node, Stack<UnionStub> union)

internal CodeWriter Write(Text text)
{
if (text) text.AppendTo(destination);
if (text.IsValid()) text.AppendTo(destination);
return this;
}
}
Expand Down
4 changes: 2 additions & 2 deletions CapnProto.net.Schema/Schema.cs
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,7 @@ public void GenerateCustomModel(CodeWriter writer)
{
if (node.Union == Node.Unions.file) continue;
var children = node.nestedNodes;
if (children)
if (children.IsValid())
{
foreach (var child in children)
if (child.id != 0) nested.Add(child.id);
Expand Down Expand Up @@ -485,7 +485,7 @@ internal static void ComputeSpace(CodeWriter writer, Node node, ref int bodyWord
{
bodyEnd = (int)((node.@struct.discriminantOffset + 1) * 16);
}
if (node.@struct.fields)
if (node.@struct.fields.IsValid())
{
foreach (var field in node.@struct.@fields)
{
Expand Down
1 change: 1 addition & 0 deletions CapnProto.net.sln
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
CapnProto-net.Schema.nuspec = CapnProto-net.Schema.nuspec
CapnProto.net.snk = CapnProto.net.snk
LICENSE = LICENSE
README.md = README.md
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{7FCA14D8-6C86-43B6-A1EC-8327468E475C}"
Expand Down
17 changes: 10 additions & 7 deletions CapnProto.net/Data.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,17 @@

namespace CapnProto
{
public struct Data
public struct Data : IPointer
{
public static explicit operator Data(Pointer pointer) { return new Data(pointer); }
public static implicit operator Pointer(Data obj) { return obj.pointer; }

private Pointer pointer;
private Data(Pointer pointer) {

this.pointer = pointer;
pointer.AssertNilOrSingleByte();
}
public static explicit operator Data(Pointer pointer) { return new Data(pointer); }
public static implicit operator Pointer(Data obj) { return obj.pointer; }
public static bool operator true(Data obj) { return obj.pointer.IsValid; }
public static bool operator false(Data obj) { return !obj.pointer.IsValid; }
public static Data Create(Pointer parent, int length) { return (Data)parent.AllocateList(ElementSize.OneByte, length); }

public int Count() { return pointer.SingleByteLength; }
Expand All @@ -26,9 +25,13 @@ public byte this[int index]
set { pointer.SetDataWord(index, unchecked((ulong)value), (ulong)0xFF); }
}

public override string ToString()
public override bool Equals(object obj)
{
return pointer.ToString();
return obj is Data && ((Data)obj).pointer == this.pointer;
}
public override int GetHashCode() { return this.pointer.GetHashCode(); }
public override string ToString() { return pointer.ToString(); }

Pointer IPointer.Pointer { get { return pointer; } }
}
}
20 changes: 10 additions & 10 deletions CapnProto.net/FixedSizeList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,18 @@
using System.Collections.Generic;
namespace CapnProto
{
public struct FixedSizeList<T> : IList<T>, IList
public struct FixedSizeList<T> : IList<T>, IList, IPointer
{
public override string ToString()
{
return pointer.ToString();
}
private readonly Pointer pointer;
private FixedSizeList(Pointer pointer) { this.pointer = pointer; }
public static explicit operator FixedSizeList<T>(Pointer pointer) { return new FixedSizeList<T>(pointer); }
public static implicit operator Pointer(FixedSizeList<T> obj) { return obj.pointer; }
public static bool operator true(FixedSizeList<T> obj) { return obj.pointer.IsValid; }
public static bool operator false(FixedSizeList<T> obj) { return !obj.pointer.IsValid; }
public static bool operator !(FixedSizeList<T> obj) { return !obj.pointer.IsValid; }
private readonly Pointer pointer;
private FixedSizeList(Pointer pointer) { this.pointer = pointer; }
public override bool Equals(object obj)
{
return obj is FixedSizeList<T> && ((FixedSizeList<T>)obj).pointer == this.pointer;
}
public override int GetHashCode() { return this.pointer.GetHashCode(); }
public override string ToString() { return pointer.ToString(); }

public T this[int index]
{
Expand Down Expand Up @@ -157,5 +156,6 @@ public static FixedSizeList<T> Create(Pointer pointer, IList<T> items)
}
return list;
}
Pointer IPointer.Pointer { get { return pointer; } }
}
}
1 change: 0 additions & 1 deletion CapnProto.net/Pointer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -554,7 +554,6 @@ internal void SetDataWord(int index, ulong value, ulong mask)
break;
case Type.ListBasic:
throw new InvalidOperationException("List values should be accessed via the SetList* methods or a list pointer");
return;
case Type.FarSingle:
case Type.FarDouble:
Dereference().SetDataWord(index, value, mask);
Expand Down
4 changes: 2 additions & 2 deletions CapnProto.net/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,5 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.0.1.0")]
[assembly: AssemblyFileVersion("0.0.1.0")]
[assembly: AssemblyVersion("0.0.4.0")]
[assembly: AssemblyFileVersion("0.0.4.0")]
17 changes: 11 additions & 6 deletions CapnProto.net/Text.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@
using System.Text;
namespace CapnProto
{
public struct Text
public struct Text : IPointer
{
public static explicit operator Text(Pointer pointer) { return new Text(pointer, null); }
public static implicit operator Pointer(Text pointer) { return pointer.pointer; }
public static explicit operator string(Text obj) { return obj.ToString(); }

private static readonly TextComparer comparer = new TextComparer();
public static TextComparer Comparer { get { return comparer; } }
public sealed class TextComparer : IComparer<Text>, IEqualityComparer<Text>
Expand Down Expand Up @@ -93,11 +97,6 @@ private Text(Pointer pointer, string value)
this.pointer = pointer;
this.value = value;
}
public static explicit operator Text(Pointer pointer) { return new Text(pointer, null); }
public static implicit operator Pointer(Text pointer) { return pointer.pointer; }
public static bool operator true(Text obj) { return obj.pointer.IsValid; }
public static bool operator false(Text obj) { return !obj.pointer.IsValid; }
public static explicit operator string(Text obj) { return obj.ToString(); }

public static Text Create(Pointer pointer, string value)
{
Expand Down Expand Up @@ -171,5 +170,11 @@ public int AppendTo(TextWriter destination)
}
return Textizer.AppendTo(pointer, destination);
}
public override bool Equals(object obj)
{
return obj is Text && ((Text)obj).pointer == this.pointer;
}
public override int GetHashCode() { return this.pointer.GetHashCode(); }
Pointer IPointer.Pointer { get { return pointer; } }
}
}
105 changes: 102 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,103 @@
capnproto-net
=============
`CapnProto-net` is a .NET implementation of the [Cap'n Proto](http://kentonv.github.io/capnproto/) data format. It is a binary format suitable for transport, exchange, storage and live in-memory usage.

Cap'n Proto bindings for .NET languages
Rather than being a *serialization* format as such, it instead describes a mapping a memory space to objects directly. This means that *there is no serialization step*: you simply work with the objects, and the in-memory representation is already serialized - it just needs to be copied to the destination. Likewise, instead of *deserializing*, you simply load a message, and access the root node.

Motivations... why would I use this?
-

Firstly, because there is no serialization/deserialization step, it is *really, really fast*. Of course, nothing is free, and you end up paying a bit more in terms of accessing each data field, but:

1. this is heavily optimized
2. you often don't access all of the fields or read all of the rows... so *why deserialize them?*

But there's another big point here:

Zero alloactions
-

CanpProto-net has been specifically written to work *with almost zero allocations*. When you're traversing data, behind the scene's each instance is actually a `struct`; you just read and write to the `struct`, and it all *just works*

> but Marc, mutable structs are evil! think of the kittens!
Yes, but you're *not actually mutating the `struct`*; the accessors are just proxies to the underlying memory space. I'm not a comp-sci person, but it probably maps as a "flyweight". Behind the scenes, you are manipulating memory formatted as defined by the Cap'n Proto specification, but you don't usually need to worry about any of those details.


Installation
-

CapnProto-net is available to [build from source](https://github.com/mgravell/capnproto-net/), but for convenience is [available on NuGet](https://www.nuget.org/packages/CapnProto-net/); to install the core library:

PM> Install-Package CapnProto-net

If you also need the schema-processing tools:

PM> Install-Package CapnProto-net.Schema

Schemas
-

Cap'n Proto data is often described via a schema file; the syntax for this file is [described in the Cap'n Proto documentation](http://kentonv.github.io/capnproto/language.htm). At the current time, `CapnProto-net` does not have a "managed" parser (although I intend writing one); it can, however, process pre-compiled schemas that have been compiled with [the `capnp` tool](http://kentonv.github.io/capnproto/capnp-tool.html). To compile a schema ***on linux***, you might use:

capnp compile -o/bin/cat {source.capnp} > {target.bin}

The tooling here will be improved shortly, but for now, a `.designer.cs` can be generated from this pre-compiled schema by referencing `CapnProto-net.Schema`, and using:

using (var msg = Message.Load(source))
{
msg.ReadNext();
var req = (CodeGeneratorRequest)msg.Root;
using (var sw = new StringWriter())
{
var codeWriter = new CSharpStructWriter(sw, req.nodes, @namespace);
req.GenerateCustomModel(codeWriter);
File.WriteAllText(destination, sw.ToString());
Console.WriteLine("File generated: {0}", destination);
}
}

Working with data
---

The core object for working with `CapnProto-net` is the `Message` object, which represents a memory workspace. We've already seen this working above, with `Message.Load`; the important thing is simply how you cast the `.Root`. In our example above, we're casting the `.Root` to a `CodeGeneratorRequest` instance, because the `capnp compile` tool *generates* a `CodeGeneratorRequest`. From there, we have access to all the properties and collections throughout the model, for example, since our model contains information about schema objects, we can list the types in our schema:

foreach (var node in req.nodes)
{
string name = node.displayName.ToString();
Console.WriteLine(" {2}: {0} ({1})", name,
name.Substring((int)node.displayNamePrefixLength), node.Union);
}

But if your `.Root` object was a `Customer` or an `Order`, then you would have all the members from your own domain model.

Note that there are sometimes multiple message instances in a single file / stream / socket / etc. To move to the next message, just call `msg.ReadNext()` - it returns `true` if a message was found, or `false` if the stream terminated. Hence a simple pattern is:

using(var msg = Message.Load(source))
{
while(msg.ReadNext())
{
ProcessData((YourDataType)msg.Root);
}
}

Creating new data
-

*Creating* new objects works a bit differently; you can't just `new` them, because they need to be mapped into our `Message`'s memory-space. However, just about every object in the model implicitly knows about the `Message`, so we don't need to pass it around everywhere. Any object in your model will do; for example:

var newAnnotation = Annotation.Create(req);

or just:

var newAnnotation = msg.Allocate<Annotation>();

We can then assign this instance to property members of other objects, or as our `.Root` object, or add it to a list:

annotations[4] = newAnnotation;

When you want to save the data:

msg.Write(destination);

or:

await msg.WriteAsync(destination);
Loading

0 comments on commit 86f5a74

Please sign in to comment.