Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

initial import

  • Loading branch information...
commit ea64f4feae7288b8b34b952d4fb1806feef12461 0 parents
Arne Claassen authored

Showing 42 changed files with 36,063 additions and 0 deletions. Show diff stats Hide diff stats

  1. 18  Firkin.Test.Perf/App.config
  2. 30  Firkin.Test.Perf/Diagnostics.cs
  3. 86  Firkin.Test.Perf/Firkin.Test.Perf.csproj
  4. 38  Firkin.Test.Perf/Properties/AssemblyInfo.cs
  5. 12  Firkin.Test.Perf/Stackoverflow/Badge.cs
  6. 15  Firkin.Test.Perf/Stackoverflow/Comment.cs
  7. 56  Firkin.Test.Perf/Stackoverflow/Post.cs
  8. 52  Firkin.Test.Perf/Stackoverflow/User.cs
  9. 31  Firkin.Test.Perf/Stackoverflow/Votes.cs
  10. 167  Firkin.Test.Perf/TStackoverflow.cs
  11. 18  Firkin.Test/App.config
  12. 81  Firkin.Test/Firkin.Test.csproj
  13. 38  Firkin.Test/Properties/AssemblyInfo.cs
  14. 51  Firkin.Test/TFirkinFile.cs
  15. 416  Firkin.Test/TFirkinHash.cs
  16. 37  Firkin.sln
  17. 26  Firkin/Data/HintRecord.cs
  18. 23  Firkin/Data/IKeySerializer.cs
  19. 32  Firkin/Data/KeyInfo.cs
  20. 26  Firkin/Data/KeyValuePair.cs
  21. 28  Firkin/Data/KeyValueRecord.cs
  22. 78  Firkin/Firkin.csproj
  23. 405  Firkin/FirkinHash.cs
  24. 30  Firkin/IFirkinHash.cs
  25. 239  Firkin/IO/FirkinFile.cs
  26. 90  Firkin/IO/FirkinHintFile.cs
  27. 25  Firkin/IO/IFirkinActiveFile.cs
  28. 24  Firkin/IO/IFirkinArchiveFile.cs
  29. 33  Firkin/IO/IFirkinFile.cs
  30. 26  Firkin/IO/IFirkinHintFile.cs
  31. 68  Firkin/IO/StreamView.cs
  32. 36  Firkin/Properties/AssemblyInfo.cs
  33. 54  Firkin/Util/ArrayEx.cs
  34. 62  Firkin/Util/StreamEx.cs
  35. 52  README.rst
  36. BIN  redist/Moq.dll
  37. 3,314  redist/Moq.xml
  38. BIN  redist/log4net.dll
  39. 28,655  redist/log4net.xml
  40. BIN  redist/nunit.framework.dll
  41. BIN  redist/protobuf-net.dll
  42. 1,591  redist/protobuf-net.xml
18  Firkin.Test.Perf/App.config
... ...
@@ -0,0 +1,18 @@
  1
+<?xml version="1.0" encoding="utf-8" ?>
  2
+<configuration>
  3
+  <configSections>
  4
+    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
  5
+  </configSections>
  6
+  <log4net>
  7
+    <!--Use this appender to log to the console.-->
  8
+    <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
  9
+      <layout type="log4net.Layout.PatternLayout">
  10
+        <conversionPattern value="%date{MM/dd HH:mm:ss} [%thread] %p : %C : %m%n" />
  11
+      </layout>
  12
+    </appender>
  13
+    <root>
  14
+      <level value="DEBUG" />
  15
+      <appender-ref ref="ConsoleAppender" />
  16
+    </root>
  17
+  </log4net>
  18
+</configuration>
30  Firkin.Test.Perf/Diagnostics.cs
... ...
@@ -0,0 +1,30 @@
  1
+/*
  2
+ * Firkin 
  3
+ * Copyright (C) 2010 Arne F. Claassen
  4
+ * http://www.claassen.net/geek/blog geekblog [at] claassen [dot] net
  5
+ *
  6
+ * Licensed under the Apache License, Version 2.0 (the "License");
  7
+ * you may not use this file except in compliance with the License.
  8
+ * You may obtain a copy of the License at
  9
+ * 
  10
+ *     http://www.apache.org/licenses/LICENSE-2.0
  11
+ * 
  12
+ * Unless required by applicable law or agreed to in writing, software
  13
+ * distributed under the License is distributed on an "AS IS" BASIS,
  14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15
+ * See the License for the specific language governing permissions and
  16
+ * limitations under the License.
  17
+ */
  18
+using System;
  19
+using System.Diagnostics;
  20
+
  21
+namespace Droog.Firkin.Test.Perf {
  22
+    public static class Diagnostics {
  23
+        public static TimeSpan Time(Action action) {
  24
+            var stopwatch = Stopwatch.StartNew();
  25
+            action();
  26
+            stopwatch.Stop();
  27
+            return stopwatch.Elapsed;
  28
+        }
  29
+    }
  30
+}
86  Firkin.Test.Perf/Firkin.Test.Perf.csproj
... ...
@@ -0,0 +1,86 @@
  1
+<?xml version="1.0" encoding="utf-8"?>
  2
+<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  3
+  <PropertyGroup>
  4
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
  5
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
  6
+    <ProductVersion>9.0.30729</ProductVersion>
  7
+    <SchemaVersion>2.0</SchemaVersion>
  8
+    <ProjectGuid>{C76884C3-3BEF-4B22-8808-4F547417B90D}</ProjectGuid>
  9
+    <OutputType>Library</OutputType>
  10
+    <AppDesignerFolder>Properties</AppDesignerFolder>
  11
+    <RootNamespace>Droog.Firkin.Test.Perf</RootNamespace>
  12
+    <AssemblyName>Droog.Firkin.Test.Perf</AssemblyName>
  13
+    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
  14
+    <FileAlignment>512</FileAlignment>
  15
+  </PropertyGroup>
  16
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
  17
+    <DebugSymbols>true</DebugSymbols>
  18
+    <DebugType>full</DebugType>
  19
+    <Optimize>false</Optimize>
  20
+    <OutputPath>bin\Debug\</OutputPath>
  21
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
  22
+    <ErrorReport>prompt</ErrorReport>
  23
+    <WarningLevel>4</WarningLevel>
  24
+  </PropertyGroup>
  25
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
  26
+    <DebugType>pdbonly</DebugType>
  27
+    <Optimize>true</Optimize>
  28
+    <OutputPath>bin\Release\</OutputPath>
  29
+    <DefineConstants>TRACE</DefineConstants>
  30
+    <ErrorReport>prompt</ErrorReport>
  31
+    <WarningLevel>4</WarningLevel>
  32
+  </PropertyGroup>
  33
+  <ItemGroup>
  34
+    <Reference Include="log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=1b44e1d426115821, processorArchitecture=MSIL">
  35
+      <SpecificVersion>False</SpecificVersion>
  36
+      <HintPath>..\redist\log4net.dll</HintPath>
  37
+    </Reference>
  38
+    <Reference Include="nunit.framework, Version=2.4.8.0, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
  39
+      <SpecificVersion>False</SpecificVersion>
  40
+      <HintPath>..\redist\nunit.framework.dll</HintPath>
  41
+    </Reference>
  42
+    <Reference Include="protobuf-net, Version=1.0.0.274, Culture=neutral, PublicKeyToken=257b51d87d2e4d67, processorArchitecture=MSIL">
  43
+      <SpecificVersion>False</SpecificVersion>
  44
+      <HintPath>..\redist\protobuf-net.dll</HintPath>
  45
+    </Reference>
  46
+    <Reference Include="System" />
  47
+    <Reference Include="System.Core">
  48
+      <RequiredTargetFramework>3.5</RequiredTargetFramework>
  49
+    </Reference>
  50
+    <Reference Include="System.Xml.Linq">
  51
+      <RequiredTargetFramework>3.5</RequiredTargetFramework>
  52
+    </Reference>
  53
+    <Reference Include="System.Data.DataSetExtensions">
  54
+      <RequiredTargetFramework>3.5</RequiredTargetFramework>
  55
+    </Reference>
  56
+    <Reference Include="System.Data" />
  57
+    <Reference Include="System.Xml" />
  58
+  </ItemGroup>
  59
+  <ItemGroup>
  60
+    <Compile Include="Diagnostics.cs" />
  61
+    <Compile Include="Stackoverflow\Comment.cs" />
  62
+    <Compile Include="Stackoverflow\Post.cs" />
  63
+    <Compile Include="Stackoverflow\User.cs" />
  64
+    <Compile Include="Stackoverflow\Votes.cs" />
  65
+    <Compile Include="TStackoverflow.cs" />
  66
+    <Compile Include="Properties\AssemblyInfo.cs" />
  67
+    <Compile Include="Stackoverflow\Badge.cs" />
  68
+  </ItemGroup>
  69
+  <ItemGroup>
  70
+    <ProjectReference Include="..\Firkin\Firkin.csproj">
  71
+      <Project>{F5F73918-C281-4A75-93E6-C2D2D148D18A}</Project>
  72
+      <Name>Firkin</Name>
  73
+    </ProjectReference>
  74
+  </ItemGroup>
  75
+  <ItemGroup>
  76
+    <None Include="App.config" />
  77
+  </ItemGroup>
  78
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
  79
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
  80
+       Other similar extension points exist, see Microsoft.Common.targets.
  81
+  <Target Name="BeforeBuild">
  82
+  </Target>
  83
+  <Target Name="AfterBuild">
  84
+  </Target>
  85
+  -->
  86
+</Project>
38  Firkin.Test.Perf/Properties/AssemblyInfo.cs
... ...
@@ -0,0 +1,38 @@
  1
+using System.Reflection;
  2
+using System.Runtime.CompilerServices;
  3
+using System.Runtime.InteropServices;
  4
+
  5
+// General Information about an assembly is controlled through the following 
  6
+// set of attributes. Change these attribute values to modify the information
  7
+// associated with an assembly.
  8
+[assembly: AssemblyTitle("Droog.Firkin.Test.Perf")]
  9
+[assembly: AssemblyDescription("")]
  10
+[assembly: AssemblyConfiguration("")]
  11
+[assembly: AssemblyCompany("")]
  12
+[assembly: AssemblyProduct("Droog.Firkin.Test.Perf")]
  13
+[assembly: AssemblyCopyright("Copyright © Arne F. Claassen 2010")]
  14
+[assembly: AssemblyTrademark("")]
  15
+[assembly: AssemblyCulture("")]
  16
+
  17
+// Setting ComVisible to false makes the types in this assembly not visible 
  18
+// to COM components.  If you need to access a type in this assembly from 
  19
+// COM, set the ComVisible attribute to true on that type.
  20
+[assembly: ComVisible(false)]
  21
+
  22
+// The following GUID is for the ID of the typelib if this project is exposed to COM
  23
+[assembly: Guid("5d3a8b58-107f-4151-9d17-fab52ea0aeef")]
  24
+
  25
+// Version information for an assembly consists of the following four values:
  26
+//
  27
+//      Major Version
  28
+//      Minor Version 
  29
+//      Build Number
  30
+//      Revision
  31
+//
  32
+// You can specify all the values or you can default the Build and Revision Numbers 
  33
+// by using the '*' as shown below:
  34
+// [assembly: AssemblyVersion("1.0.*")]
  35
+[assembly: AssemblyVersion("1.0.0.0")]
  36
+[assembly: AssemblyFileVersion("1.0.0.0")]
  37
+
  38
+[assembly: log4net.Config.XmlConfigurator(Watch = true)]
12  Firkin.Test.Perf/Stackoverflow/Badge.cs
... ...
@@ -0,0 +1,12 @@
  1
+using System;
  2
+using System.Collections.Generic;
  3
+using System.Linq;
  4
+using System.Text;
  5
+
  6
+namespace Droog.Firkin.Test.Perf.Stackoverflow {
  7
+    public class Badge {
  8
+        public int UserId;
  9
+        public string Name;
  10
+        public DateTime Date;
  11
+    }
  12
+}
15  Firkin.Test.Perf/Stackoverflow/Comment.cs
... ...
@@ -0,0 +1,15 @@
  1
+using System;
  2
+using System.Collections.Generic;
  3
+using System.Linq;
  4
+using System.Text;
  5
+
  6
+namespace Droog.Firkin.Test.Perf.Stackoverflow {
  7
+    public class Comment {
  8
+        public int Id;
  9
+        public int PostId;
  10
+        public int Score;
  11
+        public string Text;
  12
+        public DateTime CreateDate;
  13
+        public int UserId;
  14
+    }
  15
+}
56  Firkin.Test.Perf/Stackoverflow/Post.cs
... ...
@@ -0,0 +1,56 @@
  1
+using System;
  2
+using System.Collections.Generic;
  3
+using System.Linq;
  4
+using System.Text;
  5
+using System.Xml.Serialization;
  6
+
  7
+namespace Droog.Firkin.Test.Perf.Stackoverflow {
  8
+    [XmlRoot("row")]
  9
+    public class Post {
  10
+        [XmlAttribute]
  11
+        public int Id;
  12
+        [XmlAttribute]
  13
+        public PostType PostType;
  14
+        [XmlAttribute]
  15
+        public int? ParentId;
  16
+        [XmlAttribute]
  17
+        public int? AcceptedAnswerId;
  18
+        [XmlAttribute]
  19
+        public DateTime CreationDate;
  20
+        [XmlAttribute]
  21
+        public int Score;
  22
+        [XmlAttribute]
  23
+        public int ViewCount;
  24
+        [XmlAttribute]
  25
+        public string Body;
  26
+        [XmlAttribute]
  27
+        public int OwnerUserId;
  28
+        [XmlAttribute]
  29
+        public int LastEditorUserId;
  30
+        [XmlAttribute]
  31
+        public string LastEditorDisplayname;
  32
+        [XmlAttribute]
  33
+        public DateTime? LastEditDate;
  34
+        [XmlAttribute]
  35
+        public DateTime? LastActivityDate;
  36
+        [XmlAttribute]
  37
+        public DateTime? CommunityOnwedDate;
  38
+        [XmlAttribute]
  39
+        public DateTime? ClosedDate;
  40
+        [XmlAttribute]
  41
+        public string Title;
  42
+        [XmlAttribute]
  43
+        public string Tags;
  44
+        [XmlAttribute]
  45
+        public int AnswerCount;
  46
+        [XmlAttribute]
  47
+        public int CommentCount;
  48
+        [XmlAttribute]
  49
+        public int FavoriteCount;
  50
+    }
  51
+
  52
+    public enum PostType {
  53
+        Question = 1,
  54
+        Answer = 2
  55
+    }
  56
+}
52  Firkin.Test.Perf/Stackoverflow/User.cs
... ...
@@ -0,0 +1,52 @@
  1
+using System;
  2
+using System.Collections.Generic;
  3
+using System.Linq;
  4
+using System.Text;
  5
+using System.Xml.Serialization;
  6
+using ProtoBuf;
  7
+
  8
+namespace Droog.Firkin.Test.Perf.Stackoverflow {
  9
+    [ProtoContract]
  10
+    [XmlRoot("row")]
  11
+    public class User {
  12
+        [ProtoMember(1)]
  13
+        [XmlAttribute]
  14
+        public int Id;
  15
+        [ProtoMember(2)]
  16
+        [XmlAttribute]
  17
+        public int Reputation;
  18
+        [ProtoMember(3)]
  19
+        [XmlAttribute]
  20
+        public DateTime CreationDate;
  21
+        [ProtoMember(4)]
  22
+        [XmlAttribute]
  23
+        public string DisplayName;
  24
+        [ProtoMember(5)]
  25
+        [XmlAttribute]
  26
+        public string EmailHash;
  27
+        [ProtoMember(6)]
  28
+        [XmlAttribute]
  29
+        public DateTime LastAccessDate;
  30
+        [ProtoMember(7)]
  31
+        [XmlAttribute]
  32
+        public string WebsiteUrl;
  33
+        [ProtoMember(8)]
  34
+        [XmlAttribute]
  35
+        public string Location;
  36
+        [ProtoMember(9)]
  37
+        [XmlAttribute]
  38
+        public int Age;
  39
+        [ProtoMember(10)]
  40
+        [XmlAttribute]
  41
+        public string AboutMe;
  42
+        [ProtoMember(11)]
  43
+        [XmlAttribute]
  44
+        public int Views;
  45
+        [ProtoMember(12)]
  46
+        [XmlAttribute]
  47
+        public int UpVotes;
  48
+        [ProtoMember(13)]
  49
+        [XmlAttribute]
  50
+        public int DownVotes;
  51
+    }
  52
+}
31  Firkin.Test.Perf/Stackoverflow/Votes.cs
... ...
@@ -0,0 +1,31 @@
  1
+using System;
  2
+using System.Collections.Generic;
  3
+using System.Linq;
  4
+using System.Text;
  5
+
  6
+namespace Droog.Firkin.Test.Perf.Stackoverflow {
  7
+    public class Votes {
  8
+        public int Id;
  9
+        public int PostId;
  10
+        public VoteType VoteType;
  11
+        public DateTime CreationDate;
  12
+        public int? UserId;
  13
+        public int? BountyAmount;
  14
+    }
  15
+
  16
+    public enum VoteType {
  17
+        AcceptedByOriginator = 1,
  18
+        UpMod = 2,
  19
+        DownMod = 3,
  20
+        Offensive = 4,
  21
+        Favorite = 5,
  22
+        Close = 6,
  23
+        Reopen = 7,
  24
+        BountyStart = 8,
  25
+        BountyClose = 9,
  26
+        Deletion = 10,
  27
+        Undeletion = 11,
  28
+        Spam = 12,
  29
+        InformModerator = 13,
  30
+    }
  31
+}
167  Firkin.Test.Perf/TStackoverflow.cs
... ...
@@ -0,0 +1,167 @@
  1
+/*
  2
+ * Firkin 
  3
+ * Copyright (C) 2010 Arne F. Claassen
  4
+ * http://www.claassen.net/geek/blog geekblog [at] claassen [dot] net
  5
+ *
  6
+ * Licensed under the Apache License, Version 2.0 (the "License");
  7
+ * you may not use this file except in compliance with the License.
  8
+ * You may obtain a copy of the License at
  9
+ * 
  10
+ *     http://www.apache.org/licenses/LICENSE-2.0
  11
+ * 
  12
+ * Unless required by applicable law or agreed to in writing, software
  13
+ * distributed under the License is distributed on an "AS IS" BASIS,
  14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15
+ * See the License for the specific language governing permissions and
  16
+ * limitations under the License.
  17
+ */
  18
+using System;
  19
+using System.Collections.Generic;
  20
+using System.IO;
  21
+using System.Linq;
  22
+using System.Xml;
  23
+using System.Xml.Serialization;
  24
+using Droog.Firkin.Test.Perf.Stackoverflow;
  25
+using Droog.Firkin.Util;
  26
+using log4net;
  27
+using NUnit.Framework;
  28
+using ProtoBuf;
  29
+
  30
+namespace Droog.Firkin.Test.Perf {
  31
+    [TestFixture]
  32
+    public class TStackoverflow {
  33
+
  34
+        private static readonly ILog _log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
  35
+
  36
+        [Test]
  37
+        public void Read_Posts_from_Xml() {
  38
+            Console.WriteLine("All posts: {0}", Diagnostics.Time(() => {
  39
+                var i = 0;
  40
+                foreach(var user in ReadEntitiesFromXml<User>(@"C:\data\042010 SO\posts.xml")) {
  41
+                    i++;
  42
+                    //Console.WriteLine(user.DisplayName);
  43
+                }
  44
+                Console.WriteLine("Total: {0}", i);
  45
+            }));
  46
+        }
  47
+
  48
+        [Test]
  49
+        public void Read_Users_from_Xml() {
  50
+            Console.WriteLine("All users: {0}", Diagnostics.Time(() => {
  51
+                var i = 0;
  52
+                foreach(var user in ReadEntitiesFromXml<User>(@"C:\data\042010 SO\users.xml")) {
  53
+                    i++;
  54
+                    //Console.WriteLine(user.DisplayName);
  55
+                }
  56
+                Console.WriteLine("Total: {0}", i);
  57
+            }));
  58
+        }
  59
+
  60
+        [Test]
  61
+        public void Read_write_users_with_Firkin() {
  62
+            Dictionary<int, Stream> users = null;
  63
+            var elapsed = Diagnostics.Time(() => {
  64
+                users = (from user in ReadEntitiesFromXml<User>(@"C:\data\042010 SO\users.xml")
  65
+                         select new {
  66
+                             user.Id,
  67
+                             Stream = GetUserStream(user)
  68
+                         })
  69
+                    .ToDictionary(x => x.Id, y => y.Stream);
  70
+            });
  71
+            _log.DebugFormat("Read {0} users from xml: {1} ({2:0.0000}ms/user)", users.Count, elapsed, elapsed.TotalMilliseconds / users.Count);
  72
+            var path = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
  73
+            var hash = new FirkinHash<int>(path);
  74
+            try {
  75
+                elapsed = Diagnostics.Time(() => {
  76
+                    foreach(var user in users) {
  77
+                        hash.Put(user.Key, user.Value, user.Value.Length);
  78
+                    }
  79
+                });
  80
+                _log.DebugFormat("Wrote {0} users to firkin: {1} ({2:0.0000}ms/user)", users.Count, elapsed, elapsed.TotalMilliseconds / users.Count);
  81
+                var comp = new List<Stream[]>();
  82
+                elapsed = Diagnostics.Time(() => {
  83
+                    foreach(var user in users.OrderBy(x => x.Value.Length)) {
  84
+                        var stream = hash.Get(user.Key);
  85
+                        comp.Add(new[] { stream, user.Value });
  86
+                    }
  87
+                });
  88
+                _log.DebugFormat("Queried {0} users from firkin: {1} ({2:0.0000}ms/user)", users.Count, elapsed, elapsed.TotalMilliseconds / users.Count);
  89
+                foreach(var pair in comp) {
  90
+                    pair[0].Position = 0;
  91
+                    pair[1].Position = 0;
  92
+                    Assert.AreEqual(pair[0].ReadBytes(pair[0].Length), pair[1].ReadBytes(pair[1].Length));
  93
+                }
  94
+            } finally {
  95
+                hash.Dispose();
  96
+                Directory.Delete(path, true);
  97
+            }
  98
+
  99
+        }
  100
+
  101
+        [Test]
  102
+        public void Read_write_users_with_hash_reload() {
  103
+            Dictionary<int, Stream> users = null;
  104
+            var elapsed = Diagnostics.Time(() => {
  105
+                users = (from user in ReadEntitiesFromXml<User>(@"C:\data\042010 SO\users.xml")
  106
+                         select new {
  107
+                             user.Id,
  108
+                             Stream = GetUserStream(user)
  109
+                         })
  110
+                    .ToDictionary(x => x.Id, y => y.Stream);
  111
+            });
  112
+            _log.DebugFormat("Read {0} users from xml: {1} ({2:0.0000}ms/user)", users.Count, elapsed, elapsed.TotalMilliseconds / users.Count);
  113
+            var path = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
  114
+            var hash = new FirkinHash<int>(path);
  115
+            try {
  116
+                elapsed = Diagnostics.Time(() => {
  117
+                    foreach(var user in users) {
  118
+                        hash.Put(user.Key, user.Value, user.Value.Length);
  119
+                    }
  120
+                });
  121
+                _log.DebugFormat("Wrote {0} users to firkin: {1} ({2:0.0000}ms/user)", users.Count, elapsed, elapsed.TotalMilliseconds / users.Count);
  122
+                hash.Dispose();
  123
+                _log.DebugFormat("re-loading hash");
  124
+                hash = new FirkinHash<int>(path);
  125
+                var comp = new List<Stream[]>();
  126
+                elapsed = Diagnostics.Time(() => {
  127
+                    foreach(var user in users.OrderBy(x => x.Value.Length)) {
  128
+                        var stream = hash.Get(user.Key);
  129
+                        comp.Add(new[] { stream, user.Value });
  130
+                    }
  131
+                });
  132
+                _log.DebugFormat("Queried {0} users from firkin: {1} ({2:0.0000}ms/user)", users.Count, elapsed, elapsed.TotalMilliseconds / users.Count);
  133
+                foreach(var pair in comp) {
  134
+                    pair[0].Position = 0;
  135
+                    pair[1].Position = 0;
  136
+                    Assert.AreEqual(pair[0].ReadBytes(pair[0].Length), pair[1].ReadBytes(pair[1].Length));
  137
+                }
  138
+            } finally {
  139
+                hash.Dispose();
  140
+                Directory.Delete(path, true);
  141
+            }
  142
+
  143
+        }
  144
+
  145
+        private Stream GetUserStream(User user) {
  146
+            var stream = new MemoryStream();
  147
+            Serializer.Serialize(stream, user);
  148
+            stream.Position = 0;
  149
+            return stream;
  150
+        }
  151
+
  152
+        public IEnumerable<T> ReadEntitiesFromXml<T>(string filename) {
  153
+            var serializer = new XmlSerializer(typeof(T));
  154
+            var stream = new FileStream(filename, FileMode.Open);
  155
+            var reader = new XmlTextReader(stream);
  156
+            while(reader.Read()) {
  157
+                if(reader.NodeType != XmlNodeType.Element) {
  158
+                    continue;
  159
+                }
  160
+                if(reader.Name != "row") {
  161
+                    continue;
  162
+                }
  163
+                yield return (T)serializer.Deserialize(reader);
  164
+            }
  165
+        }
  166
+    }
  167
+}
18  Firkin.Test/App.config
... ...
@@ -0,0 +1,18 @@
  1
+<?xml version="1.0" encoding="utf-8" ?>
  2
+<configuration>
  3
+  <configSections>
  4
+    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
  5
+  </configSections>
  6
+  <log4net>
  7
+    <!--Use this appender to log to the console.-->
  8
+    <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
  9
+      <layout type="log4net.Layout.PatternLayout">
  10
+        <conversionPattern value="%date{MM/dd HH:mm:ss} [%thread] %p : %C : %m%n" />
  11
+      </layout>
  12
+    </appender>
  13
+    <root>
  14
+      <level value="DEBUG" />
  15
+      <appender-ref ref="ConsoleAppender" />
  16
+    </root>
  17
+  </log4net>
  18
+</configuration>
81  Firkin.Test/Firkin.Test.csproj
... ...
@@ -0,0 +1,81 @@
  1
+<?xml version="1.0" encoding="utf-8"?>
  2
+<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  3
+  <PropertyGroup>
  4
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
  5
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
  6
+    <ProductVersion>9.0.30729</ProductVersion>
  7
+    <SchemaVersion>2.0</SchemaVersion>
  8
+    <ProjectGuid>{9F4A0096-AF17-440F-85A1-9FAD16C05D36}</ProjectGuid>
  9
+    <OutputType>Library</OutputType>
  10
+    <AppDesignerFolder>Properties</AppDesignerFolder>
  11
+    <RootNamespace>Droog.Firkin.Test</RootNamespace>
  12
+    <AssemblyName>Droog.Firkin.Test</AssemblyName>
  13
+    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
  14
+    <FileAlignment>512</FileAlignment>
  15
+  </PropertyGroup>
  16
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
  17
+    <DebugSymbols>true</DebugSymbols>
  18
+    <DebugType>full</DebugType>
  19
+    <Optimize>false</Optimize>
  20
+    <OutputPath>bin\Debug\</OutputPath>
  21
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
  22
+    <ErrorReport>prompt</ErrorReport>
  23
+    <WarningLevel>4</WarningLevel>
  24
+  </PropertyGroup>
  25
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
  26
+    <DebugType>pdbonly</DebugType>
  27
+    <Optimize>true</Optimize>
  28
+    <OutputPath>bin\Release\</OutputPath>
  29
+    <DefineConstants>TRACE</DefineConstants>
  30
+    <ErrorReport>prompt</ErrorReport>
  31
+    <WarningLevel>4</WarningLevel>
  32
+  </PropertyGroup>
  33
+  <ItemGroup>
  34
+    <Reference Include="log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=1b44e1d426115821, processorArchitecture=MSIL">
  35
+      <SpecificVersion>False</SpecificVersion>
  36
+      <HintPath>..\redist\log4net.dll</HintPath>
  37
+    </Reference>
  38
+    <Reference Include="Moq, Version=4.0.812.4, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
  39
+      <SpecificVersion>False</SpecificVersion>
  40
+      <HintPath>..\redist\Moq.dll</HintPath>
  41
+    </Reference>
  42
+    <Reference Include="nunit.framework, Version=2.4.8.0, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
  43
+      <SpecificVersion>False</SpecificVersion>
  44
+      <HintPath>..\redist\nunit.framework.dll</HintPath>
  45
+    </Reference>
  46
+    <Reference Include="System" />
  47
+    <Reference Include="System.Core">
  48
+      <RequiredTargetFramework>3.5</RequiredTargetFramework>
  49
+    </Reference>
  50
+    <Reference Include="System.Xml.Linq">
  51
+      <RequiredTargetFramework>3.5</RequiredTargetFramework>
  52
+    </Reference>
  53
+    <Reference Include="System.Data.DataSetExtensions">
  54
+      <RequiredTargetFramework>3.5</RequiredTargetFramework>
  55
+    </Reference>
  56
+    <Reference Include="System.Data" />
  57
+    <Reference Include="System.Xml" />
  58
+  </ItemGroup>
  59
+  <ItemGroup>
  60
+    <Compile Include="TFirkinHash.cs" />
  61
+    <Compile Include="TFirkinFile.cs" />
  62
+    <Compile Include="Properties\AssemblyInfo.cs" />
  63
+  </ItemGroup>
  64
+  <ItemGroup>
  65
+    <ProjectReference Include="..\Firkin\Firkin.csproj">
  66
+      <Project>{F5F73918-C281-4A75-93E6-C2D2D148D18A}</Project>
  67
+      <Name>Firkin</Name>
  68
+    </ProjectReference>
  69
+  </ItemGroup>
  70
+  <ItemGroup>
  71
+    <None Include="App.config" />
  72
+  </ItemGroup>
  73
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
  74
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
  75
+       Other similar extension points exist, see Microsoft.Common.targets.
  76
+  <Target Name="BeforeBuild">
  77
+  </Target>
  78
+  <Target Name="AfterBuild">
  79
+  </Target>
  80
+  -->
  81
+</Project>
38  Firkin.Test/Properties/AssemblyInfo.cs
... ...
@@ -0,0 +1,38 @@
  1
+using System.Reflection;
  2
+using System.Runtime.CompilerServices;
  3
+using System.Runtime.InteropServices;
  4
+
  5
+// General Information about an assembly is controlled through the following 
  6
+// set of attributes. Change these attribute values to modify the information
  7
+// associated with an assembly.
  8
+[assembly: AssemblyTitle("Droog.Firkin.Test")]
  9
+[assembly: AssemblyDescription("")]
  10
+[assembly: AssemblyConfiguration("")]
  11
+[assembly: AssemblyCompany("")]
  12
+[assembly: AssemblyProduct("Droog.Firkin.Test")]
  13
+[assembly: AssemblyCopyright("Copyright © Arne F. Claassen 2010")]
  14
+[assembly: AssemblyTrademark("")]
  15
+[assembly: AssemblyCulture("")]
  16
+
  17
+// Setting ComVisible to false makes the types in this assembly not visible 
  18
+// to COM components.  If you need to access a type in this assembly from 
  19
+// COM, set the ComVisible attribute to true on that type.
  20
+[assembly: ComVisible(false)]
  21
+
  22
+// The following GUID is for the ID of the typelib if this project is exposed to COM
  23
+[assembly: Guid("e389a260-fb4a-4781-aa64-561f86802e7f")]
  24
+
  25
+// Version information for an assembly consists of the following four values:
  26
+//
  27
+//      Major Version
  28
+//      Minor Version 
  29
+//      Build Number
  30
+//      Revision
  31
+//
  32
+// You can specify all the values or you can default the Build and Revision Numbers 
  33
+// by using the '*' as shown below:
  34
+// [assembly: AssemblyVersion("1.0.*")]
  35
+[assembly: AssemblyVersion("1.0.0.0")]
  36
+[assembly: AssemblyFileVersion("1.0.0.0")]
  37
+
  38
+[assembly: log4net.Config.XmlConfigurator(Watch = true)]
51  Firkin.Test/TFirkinFile.cs
... ...
@@ -0,0 +1,51 @@
  1
+/*
  2
+ * Firkin 
  3
+ * Copyright (C) 2010 Arne F. Claassen
  4
+ * http://www.claassen.net/geek/blog geekblog [at] claassen [dot] net
  5
+ *
  6
+ * Licensed under the Apache License, Version 2.0 (the "License");
  7
+ * you may not use this file except in compliance with the License.
  8
+ * You may obtain a copy of the License at
  9
+ * 
  10
+ *     http://www.apache.org/licenses/LICENSE-2.0
  11
+ * 
  12
+ * Unless required by applicable law or agreed to in writing, software
  13
+ * distributed under the License is distributed on an "AS IS" BASIS,
  14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15
+ * See the License for the specific language governing permissions and
  16
+ * limitations under the License.
  17
+ */
  18
+using System;
  19
+using System.IO;
  20
+using Droog.Firkin.Data;
  21
+using NUnit.Framework;
  22
+
  23
+namespace Droog.Firkin.Test {
  24
+
  25
+    [TestFixture]
  26
+    public class TFirkinFile {
  27
+
  28
+        [Test]
  29
+        public void Can_rename_file() {
  30
+            var f1 = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
  31
+            var f2 = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
  32
+            var file = FirkinFile.CreateActive(f1, 1);
  33
+            var data = new MemoryStream();
  34
+            data.WriteByte(2);
  35
+            data.Position = 0;
  36
+            var keyInfo = file.Write(new KeyValuePair() { Key = new byte[] { 1 }, Value = data, ValueSize = (uint)data.Length });
  37
+            try {
  38
+                file.Rename(f2);
  39
+                Assert.IsFalse(File.Exists(f1));
  40
+                Assert.IsTrue(File.Exists(f2));
  41
+                var stream = file.ReadValue(keyInfo);
  42
+                Assert.AreEqual(1, stream.Length);
  43
+                Assert.AreEqual(2, stream.ReadByte());
  44
+                file.Dispose();
  45
+            } finally {
  46
+                File.Delete(f1);
  47
+                File.Delete(f2);
  48
+            }
  49
+        }
  50
+    }
  51
+}
416  Firkin.Test/TFirkinHash.cs
... ...
@@ -0,0 +1,416 @@
  1
+/*
  2
+ * Firkin 
  3
+ * Copyright (C) 2010 Arne F. Claassen
  4
+ * http://www.claassen.net/geek/blog geekblog [at] claassen [dot] net
  5
+ *
  6
+ * Licensed under the Apache License, Version 2.0 (the "License");
  7
+ * you may not use this file except in compliance with the License.
  8
+ * You may obtain a copy of the License at
  9
+ * 
  10
+ *     http://www.apache.org/licenses/LICENSE-2.0
  11
+ * 
  12
+ * Unless required by applicable law or agreed to in writing, software
  13
+ * distributed under the License is distributed on an "AS IS" BASIS,
  14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15
+ * See the License for the specific language governing permissions and
  16
+ * limitations under the License.
  17
+ */
  18
+using System;
  19
+using System.Collections.Generic;
  20
+using System.IO;
  21
+using System.Linq;
  22
+using log4net;
  23
+using NUnit.Framework;
  24
+using Droog.Firkin.Util;
  25
+
  26
+namespace Droog.Firkin.Test {
  27
+
  28
+    [TestFixture]
  29
+    public class TFirkinHash {
  30
+
  31
+        private static readonly ILog _log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
  32
+
  33
+        private FirkinHash<string> _hash;
  34
+        private string _path;
  35
+
  36
+        [SetUp]
  37
+        public void Setup() {
  38
+            _path = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
  39
+        }
  40
+
  41
+        public void CreateHash() {
  42
+            _hash = new FirkinHash<string>(_path);
  43
+        }
  44
+
  45
+        [TearDown]
  46
+        public void Teardown() {
  47
+            _hash.Dispose();
  48
+            Directory.Delete(_path, true);
  49
+        }
  50
+
  51
+        [Test]
  52
+        public void Can_read_write_entry() {
  53
+            CreateHash();
  54
+            var key = "foo";
  55
+            var value = "bar";
  56
+            var stream = GetStream(value);
  57
+            _hash.Put(key, stream, stream.Length);
  58
+            var stream2 = _hash.Get(key);
  59
+            Assert.AreEqual(value, GetValue<string>(stream2));
  60
+        }
  61
+
  62
+        [Test]
  63
+        public void Can_read_write_entry_with_hash_reload() {
  64
+            CreateHash();
  65
+            var key = "foo";
  66
+            var value = "bar";
  67
+            var stream = GetStream(value);
  68
+            _hash.Put(key, stream, stream.Length);
  69
+            _hash.Dispose();
  70
+            _log.DebugFormat("re-loading hash");
  71
+            CreateHash();
  72
+            var stream2 = _hash.Get(key);
  73
+            Assert.AreEqual(value, GetValue<string>(stream2));
  74
+        }
  75
+
  76
+        [Test]
  77
+        public void Can_read_write_multiple_with_hash_reload() {
  78
+            CreateHash();
  79
+            var data = Enumerable.Range(1, 5);
  80
+            foreach(var v in data) {
  81
+                var stream = GetStream(v);
  82
+                _hash.Put("k" + v, stream, stream.Length);
  83
+            }
  84
+            _hash.Dispose();
  85
+            _log.DebugFormat("re-loading hash");
  86
+            CreateHash();
  87
+            foreach(var v in data) {
  88
+                var stream2 = _hash.Get("k" + v);
  89
+                Assert.AreEqual(v, GetValue<int>(stream2));
  90
+            }
  91
+        }
  92
+
  93
+        [Test]
  94
+        public void Can_delete_record() {
  95
+            CreateHash();
  96
+            var key = "foo";
  97
+            var value = "bar";
  98
+            var stream = GetStream(value);
  99
+            _hash.Put(key, stream, stream.Length);
  100
+            _hash.Delete(key);
  101
+            Assert.IsNull(_hash.Get(key));
  102
+        }
  103
+
  104
+        [Test]
  105
+        public void Delete_persists_after_hash_reload() {
  106
+            CreateHash();
  107
+            var key = "foo";
  108
+            var value = "bar";
  109
+            var stream = GetStream(value);
  110
+            _hash.Put(key, stream, stream.Length);
  111
+            _hash.Delete(key);
  112
+            _hash.Dispose();
  113
+            _log.DebugFormat("re-loading hash");
  114
+            CreateHash();
  115
+            Assert.IsNull(_hash.Get(key));
  116
+        }
  117
+
  118
+        [Test]
  119
+        public void Can_overwrite_record() {
  120
+            CreateHash();
  121
+            var key = "foo";
  122
+            var value = "bar";
  123
+            var value2 = "baz";
  124
+            var stream = GetStream(value);
  125
+            _hash.Put(key, stream, stream.Length);
  126
+            stream = GetStream(value2);
  127
+            _hash.Put(key, stream, stream.Length);
  128
+            var stream2 = _hash.Get(key);
  129
+            Assert.AreEqual(value2, GetValue<string>(stream2));
  130
+        }
  131
+
  132
+        [Test]
  133
+        public void Overwrite_persists_after_hash_reload() {
  134
+            CreateHash();
  135
+            var key = "foo";
  136
+            var value = "bar";
  137
+            var value2 = "baz";
  138
+            var stream = GetStream(value);
  139
+            _hash.Put(key, stream, stream.Length);
  140
+            stream = GetStream(value2);
  141
+            _hash.Put(key, stream, stream.Length);
  142
+            _hash.Dispose();
  143
+            _log.DebugFormat("re-loading hash");
  144
+            CreateHash();
  145
+            var stream2 = _hash.Get(key);
  146
+            Assert.AreEqual(value2, GetValue<string>(stream2));
  147
+        }
  148
+
  149
+        [Test]
  150
+        public void Can_delete_and_write_record_again() {
  151
+            CreateHash();
  152
+            var key = "foo";
  153
+            var value = "bar";
  154
+            var value2 = "baz";
  155
+            var stream = GetStream(value);
  156
+            _hash.Put(key, stream, stream.Length);
  157
+            _hash.Delete(key);
  158
+            stream = GetStream(value2);
  159
+            _hash.Put(key, stream, stream.Length);
  160
+            var stream2 = _hash.Get(key);
  161
+            Assert.AreEqual(value2, GetValue<string>(stream2));
  162
+        }
  163
+
  164
+        [Test]
  165
+        public void Can_delete_and_write_record_again_after_hash_reload() {
  166
+            CreateHash();
  167
+            var key = "foo";
  168
+            var value = "bar";
  169
+            var value2 = "baz";
  170
+            var stream = GetStream(value);
  171
+            _hash.Put(key, stream, stream.Length);
  172
+            _hash.Delete(key);
  173
+            _hash.Dispose();
  174
+            _log.DebugFormat("re-loading hash");
  175
+            CreateHash();
  176
+            stream = GetStream(value2);
  177
+            _hash.Put(key, stream, stream.Length);
  178
+            var stream2 = _hash.Get(key);
  179
+            Assert.AreEqual(value2, GetValue<string>(stream2));
  180
+        }
  181
+
  182
+        [Test]
  183
+        public void Active_rolls_over_at_size_barrier() {
  184
+            _hash = new FirkinHash<string>(_path, 30);
  185
+            var stream = GetStream("bar");
  186
+            _hash.Put("foo1", stream, stream.Length);
  187
+            stream.Position = 0;
  188
+            _hash.Put("foo2", stream, stream.Length);
  189
+            stream.Position = 0;
  190
+            _hash.Put("foo3", stream, stream.Length);
  191
+            stream.Position = 0;
  192
+            Assert.AreEqual(4, Directory.GetFiles(_path).Count());
  193
+        }
  194
+
  195
+        [Test]
  196
+        public void Can_access_keys_across_files_after_hash_reload() {
  197
+            _hash = new FirkinHash<string>(_path, 30);
  198
+            var stream = GetStream("bar1");
  199
+            _hash.Put("foo1", stream, stream.Length);
  200
+            stream = GetStream("bar2");
  201
+            _hash.Put("foo2", stream, stream.Length);
  202
+            stream = GetStream("bar3");
  203
+            _hash.Put("foo3", stream, stream.Length);
  204
+            _hash.Dispose();
  205
+            _hash = new FirkinHash<string>(_path, 30);
  206
+            Assert.AreEqual("bar3", GetValue<string>(_hash.Get("foo3")));
  207
+            Assert.AreEqual("bar1", GetValue<string>(_hash.Get("foo1")));
  208
+            Assert.AreEqual("bar2", GetValue<string>(_hash.Get("foo2")));
  209
+        }
  210
+
  211
+        [Test]
  212
+        public void Can_call_merge_and_retrieve_data() {
  213
+            _hash = new FirkinHash<string>(_path, 60);
  214
+            var stream = GetStream("bar1");
  215
+            _hash.Put("foo1", stream, stream.Length);
  216
+            stream = GetStream("bar2");
  217
+            _hash.Put("foo2", stream, stream.Length);
  218
+            stream = GetStream("bar3");
  219
+            _hash.Put("foo3", stream, stream.Length);
  220
+            stream = GetStream("bar4");
  221
+            _hash.Put("foo4", stream, stream.Length);
  222
+            stream = GetStream("bar1x");
  223
+            _hash.Put("foo1", stream, stream.Length);
  224
+            _hash.Merge();
  225
+            Assert.AreEqual(4,_hash.Count);
  226
+            Assert.AreEqual("bar3", GetValue<string>(_hash.Get("foo3")));
  227
+            Assert.AreEqual("bar1x", GetValue<string>(_hash.Get("foo1")));
  228
+            Assert.AreEqual("bar2", GetValue<string>(_hash.Get("foo2")));
  229
+            Assert.AreEqual("bar4", GetValue<string>(_hash.Get("foo4")));
  230
+        }
  231
+
  232
+        [Test]
  233
+        public void Can_call_merge_and_reload_hash_then_retrieve_data() {
  234
+            _hash = new FirkinHash<string>(_path, 30);
  235
+            var stream = GetStream("bar1");
  236
+            _hash.Put("foo1", stream, stream.Length);
  237
+            stream = GetStream("bar2");
  238
+            _hash.Put("foo2", stream, stream.Length);
  239
+            stream = GetStream("bar3");
  240
+            _hash.Put("foo3", stream, stream.Length);
  241
+            stream = GetStream("bar4");
  242
+            _hash.Put("foo4", stream, stream.Length);
  243
+            stream = GetStream("bar1x");
  244
+            _hash.Put("foo1", stream, stream.Length);
  245
+            _hash.Merge();
  246
+            _hash.Dispose();
  247
+            _log.DebugFormat("re-loading hash");
  248
+            _hash = new FirkinHash<string>(_path, 30);
  249
+            Assert.AreEqual("bar3", GetValue<string>(_hash.Get("foo3")));
  250
+            Assert.AreEqual("bar1x", GetValue<string>(_hash.Get("foo1")));
  251
+            Assert.AreEqual("bar2", GetValue<string>(_hash.Get("foo2")));
  252
+            Assert.AreEqual("bar4", GetValue<string>(_hash.Get("foo4")));
  253
+        }
  254
+
  255
+        [Test]
  256
+        public void Writing_empty_stream_is_a_delete() {
  257
+            CreateHash();
  258
+            _hash.Put("foo",new MemoryStream(),0);
  259
+            Assert.AreEqual(0,_hash.Count);
  260
+            Assert.IsNull(_hash.Get("foo"));
  261
+        }
  262
+
  263
+        [Test]
  264
+        public void Read_write_delete_consistency() {
  265
+            var r = new Random(1234);
  266
+            CreateHash();
  267
+            var dictionary = new Dictionary<string, byte[]>();
  268
+            for(var i = 0; i < 1000; i++) {
  269
+                var k = "k" + r.Next(100);
  270
+                if(r.Next(4) == 3) {
  271
+                    dictionary.Remove(k);
  272
+                    _hash.Delete(k);
  273
+                } else {
  274
+                    var v = GetRandomBytes(r);
  275
+                    dictionary[k] = v;
  276
+                    _hash.Put(k, GetStream(v), v.Length);
  277
+                }
  278
+                _hash.Get("k" + r.Next(100));
  279
+            }
  280
+            Assert.AreEqual(dictionary.Count, _hash.Count);
  281
+            foreach(var pair in dictionary) {
  282
+                Assert.AreEqual(0, pair.Value.Compare(_hash.Get(pair.Key).ReadBytes()));
  283
+            }
  284
+        }
  285
+
  286
+        [Test]
  287
+        public void Read_write_delete_consistency_with_reload_before_read() {
  288
+            var r = new Random(1234);
  289
+            CreateHash();
  290
+            var dictionary = new Dictionary<string, byte[]>();
  291
+            for(var i = 0; i < 1000; i++) {
  292
+                var k = "k" + r.Next(100);
  293
+                if(r.Next(4) == 3) {
  294
+                    dictionary.Remove(k);
  295
+                    _hash.Delete(k);
  296
+                } else {
  297
+                    var v = GetRandomBytes(r);
  298
+                    dictionary[k] = v;
  299
+                    _hash.Put(k, GetStream(v), v.Length);
  300
+                }
  301
+                _hash.Get("k" + r.Next(100));
  302
+            }
  303
+            _hash.Dispose();
  304
+            _log.DebugFormat("re-loading hash");
  305
+            CreateHash();
  306
+            Assert.AreEqual(dictionary.Count, _hash.Count);
  307
+            foreach(var pair in dictionary) {
  308
+                Assert.AreEqual(0, pair.Value.Compare(_hash.Get(pair.Key).ReadBytes()));
  309
+            }
  310
+        }
  311
+
  312
+        [Test]
  313
+        public void Read_write_delete_consistency_with_merge_before_read() {
  314
+            var r = new Random(1234);
  315
+            _hash = new FirkinHash<string>(_path, 10*1024);
  316
+            var dictionary = new Dictionary<string, byte[]>();
  317
+            for(var i = 0; i < 200; i++) {
  318
+                var k = "k" + r.Next(100);
  319
+                if(r.Next(4) == 3) {
  320
+                    dictionary.Remove(k);
  321
+                    _hash.Delete(k);
  322
+                } else {
  323
+                    var v = GetRandomBytes(r);
  324
+                    dictionary[k] = v;
  325
+                    _hash.Put(k, GetStream(v), v.Length);
  326
+                }
  327
+                _hash.Get("k" + r.Next(100));
  328
+            }
  329
+            _hash.Merge();
  330
+            Assert.AreEqual(dictionary.Count, _hash.Count);
  331
+            foreach(var pair in dictionary) {
  332
+                Assert.AreEqual(0, pair.Value.Compare(_hash.Get(pair.Key).ReadBytes()));
  333
+            }
  334
+        }
  335
+
  336
+        [Test]
  337
+        public void Read_write_delete_consistency_with_merge_in_middle() {
  338
+            var r = new Random(1234);
  339
+            _hash = new FirkinHash<string>(_path, 10 * 1024);
  340
+            var dictionary = new Dictionary<string, byte[]>();
  341
+            for(var i = 0; i < 500; i++) {
  342
+                var k = "k" + r.Next(100);
  343
+                if(r.Next(4) == 3) {
  344
+                    dictionary.Remove(k);
  345
+                    _hash.Delete(k);
  346
+                } else {
  347
+                    var v = GetRandomBytes(r);
  348
+                    dictionary[k] = v;
  349
+                    _hash.Put(k, GetStream(v), v.Length);
  350
+                }
  351
+                _hash.Get("k" + r.Next(100));
  352
+            }
  353
+            _hash.Merge();
  354
+            for(var i = 0; i < 500; i++) {
  355
+                var k = "k" + r.Next(100);
  356
+                if(r.Next(5) == 5) {
  357
+                    dictionary.Remove(k);
  358
+                    _hash.Delete(k);
  359
+                } else {
  360
+                    var v = GetRandomBytes(r);
  361
+                    dictionary[k] = v;
  362
+                    _hash.Put(k, GetStream(v), v.Length);
  363
+                }
  364
+                _hash.Get("k" + r.Next(100));
  365
+            }
  366
+            Assert.AreEqual(dictionary.Count, _hash.Count);
  367
+            foreach(var pair in dictionary) {
  368
+                Assert.AreEqual(0, pair.Value.Compare(_hash.Get(pair.Key).ReadBytes()));
  369
+            }
  370
+        }
  371
+
  372
+        private byte[] GetRandomBytes(Random r) {
  373
+            var bytes = new byte[r.Next(50)+50];
  374
+            for(var i = 0; i < bytes.Length; i++) {
  375
+                bytes[i] = (byte)(r.Next(30) + 10);
  376
+            }
  377
+            return bytes;
  378
+        }
  379
+
  380
+        private Stream GetStream(byte[] bytes) {
  381
+            var stream = new MemoryStream();
  382
+            stream.Write(bytes, 0, bytes.Length);
  383
+            stream.Position = 0;
  384
+            return stream;
  385
+        }
  386
+
  387
+        private Stream GetStream(int value) {
  388
+            var stream = new MemoryStream();
  389
+            var bytes = BitConverter.GetBytes(value);
  390
+            stream.Write(bytes, 0, bytes.Length);
  391
+            stream.Position = 0;
  392
+            return stream;
  393
+        }
  394
+
  395
+        private Stream GetStream(string value) {
  396
+            var stream = new MemoryStream();
  397
+            var writer = new StreamWriter(stream);
  398
+            writer.Write(value);
  399
+            writer.Flush();
  400
+            stream.Position = 0;
  401
+            return stream;
  402
+        }
  403
+
  404
+        private T GetValue<T>(Stream stream) {
  405
+            if(typeof(T) == typeof(int)) {
  406
+                var bytes = new Byte[4];
  407
+                stream.Read(bytes, 0, 4);
  408
+                return (T)(object)BitConverter.ToInt32(bytes, 0);
  409