First official checkin.
authorJens Alfke <jens@mooseyard.com>
Sun Sep 20 15:14:12 2009 -0700 (2009-09-20)
changeset 031a43d94cc26
child 1 6cbad782d16a
First official checkin.
LICENSE.txt
Ottoman.xcodeproj/project.pbxproj
include/Base.h
include/Chunk.h
include/Dictionary.h
include/File.h
include/Hash.h
include/Index.h
include/MemoryMap.h
include/Ottoman.h
include/VersionDictionary.h
src/Base.cpp
src/Chunk.cpp
src/Dictionary.cpp
src/File.cpp
src/Hash.cpp
src/Index.cpp
src/MemoryMap.cpp
src/Ottoman.cpp
src/VersionDictionary.cpp
test/Dictionary_test.cpp
test/Hash_test.cpp
test/Ottoman_test.cpp
test/TestUtils.cpp
test/TestUtils.h
test/VersionDictionary_test.cpp
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/LICENSE.txt	Sun Sep 20 15:14:12 2009 -0700
     1.3 @@ -0,0 +1,23 @@
     1.4 +[This is a standard BSD license.]
     1.5 +
     1.6 +Copyright (c) 2009, Jens Alfke <jens@mooseyard.com>. All rights reserved.
     1.7 +
     1.8 +Redistribution and use in source and binary forms, with or without modification,
     1.9 +are permitted provided that the following conditions are met:
    1.10 +
    1.11 +* Redistributions of source code must retain the above copyright notice, this
    1.12 +  list of conditions and the following disclaimer.
    1.13 +* Redistributions in binary form must reproduce the above copyright notice, this
    1.14 +  list of conditions and the following disclaimer in the documentation and/or
    1.15 +  other materials provided with the distribution.
    1.16 +
    1.17 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
    1.18 +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    1.19 +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    1.20 +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
    1.21 +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
    1.22 +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
    1.23 +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
    1.24 +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
    1.25 +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
    1.26 +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/Ottoman.xcodeproj/project.pbxproj	Sun Sep 20 15:14:12 2009 -0700
     2.3 @@ -0,0 +1,322 @@
     2.4 +// !$*UTF8*$!
     2.5 +{
     2.6 +	archiveVersion = 1;
     2.7 +	classes = {
     2.8 +	};
     2.9 +	objectVersion = 45;
    2.10 +	objects = {
    2.11 +
    2.12 +/* Begin PBXBuildFile section */
    2.13 +		27156CAA104C9C44009EBD39 /* gtest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27156CA9104C9C44009EBD39 /* gtest.framework */; };
    2.14 +		27603901105AC81200D931A7 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27603900105AC81200D931A7 /* CoreFoundation.framework */; };
    2.15 +		276E5BCD1066D13D008A2171 /* Base.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 276E5BC41066D13D008A2171 /* Base.cpp */; };
    2.16 +		276E5BCE1066D13D008A2171 /* Chunk.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 276E5BC51066D13D008A2171 /* Chunk.cpp */; };
    2.17 +		276E5BCF1066D13D008A2171 /* Dictionary.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 276E5BC61066D13D008A2171 /* Dictionary.cpp */; };
    2.18 +		276E5BD01066D13D008A2171 /* File.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 276E5BC71066D13D008A2171 /* File.cpp */; };
    2.19 +		276E5BD11066D13D008A2171 /* Hash.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 276E5BC81066D13D008A2171 /* Hash.cpp */; };
    2.20 +		276E5BD21066D13D008A2171 /* Index.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 276E5BC91066D13D008A2171 /* Index.cpp */; };
    2.21 +		276E5BD31066D13D008A2171 /* MemoryMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 276E5BCA1066D13D008A2171 /* MemoryMap.cpp */; };
    2.22 +		276E5BD41066D13D008A2171 /* Ottoman.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 276E5BCB1066D13D008A2171 /* Ottoman.cpp */; };
    2.23 +		276E5BD51066D13D008A2171 /* VersionDictionary.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 276E5BCC1066D13D008A2171 /* VersionDictionary.cpp */; };
    2.24 +		276E5BDD1066D142008A2171 /* Dictionary_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 276E5BD71066D142008A2171 /* Dictionary_test.cpp */; };
    2.25 +		276E5BDE1066D142008A2171 /* Hash_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 276E5BD81066D142008A2171 /* Hash_test.cpp */; };
    2.26 +		276E5BDF1066D142008A2171 /* Ottoman_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 276E5BD91066D142008A2171 /* Ottoman_test.cpp */; };
    2.27 +		276E5BE01066D142008A2171 /* TestUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 276E5BDA1066D142008A2171 /* TestUtils.cpp */; };
    2.28 +		276E5BE11066D142008A2171 /* VersionDictionary_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 276E5BDC1066D142008A2171 /* VersionDictionary_test.cpp */; };
    2.29 +/* End PBXBuildFile section */
    2.30 +
    2.31 +/* Begin PBXCopyFilesBuildPhase section */
    2.32 +		8DD76F690486A84900D96B5E /* CopyFiles */ = {
    2.33 +			isa = PBXCopyFilesBuildPhase;
    2.34 +			buildActionMask = 8;
    2.35 +			dstPath = /usr/share/man/man1/;
    2.36 +			dstSubfolderSpec = 0;
    2.37 +			files = (
    2.38 +			);
    2.39 +			runOnlyForDeploymentPostprocessing = 1;
    2.40 +		};
    2.41 +/* End PBXCopyFilesBuildPhase section */
    2.42 +
    2.43 +/* Begin PBXFileReference section */
    2.44 +		27156CA9104C9C44009EBD39 /* gtest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = gtest.framework; path = /Library/Frameworks/gtest.framework; sourceTree = "<absolute>"; };
    2.45 +		27603900105AC81200D931A7 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; };
    2.46 +		276E5BBA1066D135008A2171 /* Base.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Base.h; sourceTree = "<group>"; };
    2.47 +		276E5BBB1066D135008A2171 /* Chunk.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Chunk.h; path = ../include/Chunk.h; sourceTree = "<group>"; };
    2.48 +		276E5BBC1066D135008A2171 /* Dictionary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Dictionary.h; sourceTree = "<group>"; };
    2.49 +		276E5BBD1066D135008A2171 /* File.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = File.h; sourceTree = "<group>"; };
    2.50 +		276E5BBE1066D135008A2171 /* Hash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Hash.h; path = ../include/Hash.h; sourceTree = "<group>"; };
    2.51 +		276E5BBF1066D135008A2171 /* Index.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Index.h; path = ../include/Index.h; sourceTree = "<group>"; };
    2.52 +		276E5BC01066D135008A2171 /* MemoryMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MemoryMap.h; path = ../include/MemoryMap.h; sourceTree = "<group>"; };
    2.53 +		276E5BC11066D135008A2171 /* Ottoman.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Ottoman.h; sourceTree = "<group>"; };
    2.54 +		276E5BC21066D135008A2171 /* VersionDictionary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VersionDictionary.h; sourceTree = "<group>"; };
    2.55 +		276E5BC41066D13D008A2171 /* Base.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Base.cpp; sourceTree = "<group>"; };
    2.56 +		276E5BC51066D13D008A2171 /* Chunk.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Chunk.cpp; sourceTree = "<group>"; };
    2.57 +		276E5BC61066D13D008A2171 /* Dictionary.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Dictionary.cpp; sourceTree = "<group>"; };
    2.58 +		276E5BC71066D13D008A2171 /* File.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = File.cpp; sourceTree = "<group>"; };
    2.59 +		276E5BC81066D13D008A2171 /* Hash.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Hash.cpp; sourceTree = "<group>"; };
    2.60 +		276E5BC91066D13D008A2171 /* Index.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Index.cpp; sourceTree = "<group>"; };
    2.61 +		276E5BCA1066D13D008A2171 /* MemoryMap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MemoryMap.cpp; sourceTree = "<group>"; };
    2.62 +		276E5BCB1066D13D008A2171 /* Ottoman.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Ottoman.cpp; sourceTree = "<group>"; };
    2.63 +		276E5BCC1066D13D008A2171 /* VersionDictionary.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VersionDictionary.cpp; sourceTree = "<group>"; };
    2.64 +		276E5BD71066D142008A2171 /* Dictionary_test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Dictionary_test.cpp; sourceTree = "<group>"; };
    2.65 +		276E5BD81066D142008A2171 /* Hash_test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Hash_test.cpp; sourceTree = "<group>"; };
    2.66 +		276E5BD91066D142008A2171 /* Ottoman_test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Ottoman_test.cpp; sourceTree = "<group>"; };
    2.67 +		276E5BDA1066D142008A2171 /* TestUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TestUtils.cpp; sourceTree = "<group>"; };
    2.68 +		276E5BDB1066D142008A2171 /* TestUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TestUtils.h; sourceTree = "<group>"; };
    2.69 +		276E5BDC1066D142008A2171 /* VersionDictionary_test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VersionDictionary_test.cpp; sourceTree = "<group>"; };
    2.70 +		8DD76F6C0486A84900D96B5E /* SafeStorageTest */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = SafeStorageTest; sourceTree = BUILT_PRODUCTS_DIR; };
    2.71 +/* End PBXFileReference section */
    2.72 +
    2.73 +/* Begin PBXFrameworksBuildPhase section */
    2.74 +		8DD76F660486A84900D96B5E /* Frameworks */ = {
    2.75 +			isa = PBXFrameworksBuildPhase;
    2.76 +			buildActionMask = 2147483647;
    2.77 +			files = (
    2.78 +				27156CAA104C9C44009EBD39 /* gtest.framework in Frameworks */,
    2.79 +				27603901105AC81200D931A7 /* CoreFoundation.framework in Frameworks */,
    2.80 +			);
    2.81 +			runOnlyForDeploymentPostprocessing = 0;
    2.82 +		};
    2.83 +/* End PBXFrameworksBuildPhase section */
    2.84 +
    2.85 +/* Begin PBXGroup section */
    2.86 +		08FB7794FE84155DC02AAC07 /* BPlusTree */ = {
    2.87 +			isa = PBXGroup;
    2.88 +			children = (
    2.89 +				276E5BB91066D135008A2171 /* include */,
    2.90 +				276E5BC31066D13D008A2171 /* src */,
    2.91 +				276E5BD61066D142008A2171 /* test */,
    2.92 +				27603900105AC81200D931A7 /* CoreFoundation.framework */,
    2.93 +				27156CA9104C9C44009EBD39 /* gtest.framework */,
    2.94 +				1AB674ADFE9D54B511CA2CBB /* Products */,
    2.95 +			);
    2.96 +			name = BPlusTree;
    2.97 +			sourceTree = "<group>";
    2.98 +		};
    2.99 +		1AB674ADFE9D54B511CA2CBB /* Products */ = {
   2.100 +			isa = PBXGroup;
   2.101 +			children = (
   2.102 +				8DD76F6C0486A84900D96B5E /* SafeStorageTest */,
   2.103 +			);
   2.104 +			name = Products;
   2.105 +			sourceTree = "<group>";
   2.106 +		};
   2.107 +		276E5BB91066D135008A2171 /* include */ = {
   2.108 +			isa = PBXGroup;
   2.109 +			children = (
   2.110 +				276E5BBA1066D135008A2171 /* Base.h */,
   2.111 +				276E5BBC1066D135008A2171 /* Dictionary.h */,
   2.112 +				276E5BBD1066D135008A2171 /* File.h */,
   2.113 +				276E5BC11066D135008A2171 /* Ottoman.h */,
   2.114 +				276E5BC21066D135008A2171 /* VersionDictionary.h */,
   2.115 +			);
   2.116 +			path = include;
   2.117 +			sourceTree = "<group>";
   2.118 +		};
   2.119 +		276E5BC31066D13D008A2171 /* src */ = {
   2.120 +			isa = PBXGroup;
   2.121 +			children = (
   2.122 +				276E5BC41066D13D008A2171 /* Base.cpp */,
   2.123 +				276E5BBB1066D135008A2171 /* Chunk.h */,
   2.124 +				276E5BC51066D13D008A2171 /* Chunk.cpp */,
   2.125 +				276E5BC61066D13D008A2171 /* Dictionary.cpp */,
   2.126 +				276E5BC71066D13D008A2171 /* File.cpp */,
   2.127 +				276E5BBE1066D135008A2171 /* Hash.h */,
   2.128 +				276E5BC81066D13D008A2171 /* Hash.cpp */,
   2.129 +				276E5BBF1066D135008A2171 /* Index.h */,
   2.130 +				276E5BC91066D13D008A2171 /* Index.cpp */,
   2.131 +				276E5BC01066D135008A2171 /* MemoryMap.h */,
   2.132 +				276E5BCA1066D13D008A2171 /* MemoryMap.cpp */,
   2.133 +				276E5BCB1066D13D008A2171 /* Ottoman.cpp */,
   2.134 +				276E5BCC1066D13D008A2171 /* VersionDictionary.cpp */,
   2.135 +			);
   2.136 +			path = src;
   2.137 +			sourceTree = "<group>";
   2.138 +		};
   2.139 +		276E5BD61066D142008A2171 /* test */ = {
   2.140 +			isa = PBXGroup;
   2.141 +			children = (
   2.142 +				276E5BD71066D142008A2171 /* Dictionary_test.cpp */,
   2.143 +				276E5BD81066D142008A2171 /* Hash_test.cpp */,
   2.144 +				276E5BD91066D142008A2171 /* Ottoman_test.cpp */,
   2.145 +				276E5BDC1066D142008A2171 /* VersionDictionary_test.cpp */,
   2.146 +				276E5BDB1066D142008A2171 /* TestUtils.h */,
   2.147 +				276E5BDA1066D142008A2171 /* TestUtils.cpp */,
   2.148 +			);
   2.149 +			path = test;
   2.150 +			sourceTree = "<group>";
   2.151 +		};
   2.152 +/* End PBXGroup section */
   2.153 +
   2.154 +/* Begin PBXNativeTarget section */
   2.155 +		8DD76F620486A84900D96B5E /* OttomanTest */ = {
   2.156 +			isa = PBXNativeTarget;
   2.157 +			buildConfigurationList = 1DEB923108733DC60010E9CD /* Build configuration list for PBXNativeTarget "OttomanTest" */;
   2.158 +			buildPhases = (
   2.159 +				8DD76F640486A84900D96B5E /* Sources */,
   2.160 +				8DD76F660486A84900D96B5E /* Frameworks */,
   2.161 +				8DD76F690486A84900D96B5E /* CopyFiles */,
   2.162 +			);
   2.163 +			buildRules = (
   2.164 +			);
   2.165 +			dependencies = (
   2.166 +			);
   2.167 +			name = OttomanTest;
   2.168 +			productInstallPath = "$(HOME)/bin";
   2.169 +			productName = BPlusTree;
   2.170 +			productReference = 8DD76F6C0486A84900D96B5E /* SafeStorageTest */;
   2.171 +			productType = "com.apple.product-type.tool";
   2.172 +		};
   2.173 +/* End PBXNativeTarget section */
   2.174 +
   2.175 +/* Begin PBXProject section */
   2.176 +		08FB7793FE84155DC02AAC07 /* Project object */ = {
   2.177 +			isa = PBXProject;
   2.178 +			buildConfigurationList = 1DEB923508733DC60010E9CD /* Build configuration list for PBXProject "Ottoman" */;
   2.179 +			compatibilityVersion = "Xcode 3.1";
   2.180 +			hasScannedForEncodings = 1;
   2.181 +			mainGroup = 08FB7794FE84155DC02AAC07 /* BPlusTree */;
   2.182 +			projectDirPath = "";
   2.183 +			projectRoot = "";
   2.184 +			targets = (
   2.185 +				8DD76F620486A84900D96B5E /* OttomanTest */,
   2.186 +			);
   2.187 +		};
   2.188 +/* End PBXProject section */
   2.189 +
   2.190 +/* Begin PBXSourcesBuildPhase section */
   2.191 +		8DD76F640486A84900D96B5E /* Sources */ = {
   2.192 +			isa = PBXSourcesBuildPhase;
   2.193 +			buildActionMask = 2147483647;
   2.194 +			files = (
   2.195 +				276E5BCD1066D13D008A2171 /* Base.cpp in Sources */,
   2.196 +				276E5BCE1066D13D008A2171 /* Chunk.cpp in Sources */,
   2.197 +				276E5BCF1066D13D008A2171 /* Dictionary.cpp in Sources */,
   2.198 +				276E5BD01066D13D008A2171 /* File.cpp in Sources */,
   2.199 +				276E5BD11066D13D008A2171 /* Hash.cpp in Sources */,
   2.200 +				276E5BD21066D13D008A2171 /* Index.cpp in Sources */,
   2.201 +				276E5BD31066D13D008A2171 /* MemoryMap.cpp in Sources */,
   2.202 +				276E5BD41066D13D008A2171 /* Ottoman.cpp in Sources */,
   2.203 +				276E5BD51066D13D008A2171 /* VersionDictionary.cpp in Sources */,
   2.204 +				276E5BDD1066D142008A2171 /* Dictionary_test.cpp in Sources */,
   2.205 +				276E5BDE1066D142008A2171 /* Hash_test.cpp in Sources */,
   2.206 +				276E5BDF1066D142008A2171 /* Ottoman_test.cpp in Sources */,
   2.207 +				276E5BE01066D142008A2171 /* TestUtils.cpp in Sources */,
   2.208 +				276E5BE11066D142008A2171 /* VersionDictionary_test.cpp in Sources */,
   2.209 +			);
   2.210 +			runOnlyForDeploymentPostprocessing = 0;
   2.211 +		};
   2.212 +/* End PBXSourcesBuildPhase section */
   2.213 +
   2.214 +/* Begin XCBuildConfiguration section */
   2.215 +		1DEB923208733DC60010E9CD /* Debug */ = {
   2.216 +			isa = XCBuildConfiguration;
   2.217 +			buildSettings = {
   2.218 +				ALWAYS_SEARCH_USER_PATHS = NO;
   2.219 +				COPY_PHASE_STRIP = NO;
   2.220 +				FRAMEWORK_SEARCH_PATHS = (
   2.221 +					"$(inherited)",
   2.222 +					/Code/googletest/xcode/build/Debug,
   2.223 +				);
   2.224 +				GCC_DYNAMIC_NO_PIC = NO;
   2.225 +				GCC_ENABLE_FIX_AND_CONTINUE = YES;
   2.226 +				GCC_MODEL_TUNING = G5;
   2.227 +				GCC_OPTIMIZATION_LEVEL = 0;
   2.228 +				GCC_PREPROCESSOR_DEFINITIONS = "";
   2.229 +				INSTALL_PATH = /usr/local/bin;
   2.230 +				LIBRARY_SEARCH_PATHS = (
   2.231 +					"$(inherited)",
   2.232 +					/Code/googletest/xcode/build/Debug,
   2.233 +				);
   2.234 +				PRODUCT_NAME = SafeStorageTest;
   2.235 +			};
   2.236 +			name = Debug;
   2.237 +		};
   2.238 +		1DEB923308733DC60010E9CD /* Release */ = {
   2.239 +			isa = XCBuildConfiguration;
   2.240 +			buildSettings = {
   2.241 +				ALWAYS_SEARCH_USER_PATHS = NO;
   2.242 +				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
   2.243 +				FRAMEWORK_SEARCH_PATHS = (
   2.244 +					"$(inherited)",
   2.245 +					/Code/googletest/xcode/build/Debug,
   2.246 +				);
   2.247 +				GCC_MODEL_TUNING = G5;
   2.248 +				INSTALL_PATH = /usr/local/bin;
   2.249 +				LIBRARY_SEARCH_PATHS = (
   2.250 +					"$(inherited)",
   2.251 +					/Code/googletest/xcode/build/Debug,
   2.252 +				);
   2.253 +				PRODUCT_NAME = SafeStorageTest;
   2.254 +			};
   2.255 +			name = Release;
   2.256 +		};
   2.257 +		1DEB923608733DC60010E9CD /* Debug */ = {
   2.258 +			isa = XCBuildConfiguration;
   2.259 +			buildSettings = {
   2.260 +				ARCHS = "$(ARCHS_STANDARD_32_BIT)";
   2.261 +				GCC_C_LANGUAGE_STANDARD = c99;
   2.262 +				GCC_OPTIMIZATION_LEVEL = 0;
   2.263 +				GCC_PRECOMPILE_PREFIX_HEADER = YES;
   2.264 +				GCC_PREPROCESSOR_DEFINITIONS = "";
   2.265 +				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
   2.266 +				GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
   2.267 +				GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
   2.268 +				GCC_WARN_ABOUT_RETURN_TYPE = YES;
   2.269 +				GCC_WARN_EFFECTIVE_CPLUSPLUS_VIOLATIONS = NO;
   2.270 +				GCC_WARN_SHADOW = NO;
   2.271 +				GCC_WARN_UNUSED_VARIABLE = YES;
   2.272 +				ONLY_ACTIVE_ARCH = YES;
   2.273 +				PREBINDING = NO;
   2.274 +				SDKROOT = macosx10.5;
   2.275 +				WARNING_CFLAGS = "-Wall";
   2.276 +			};
   2.277 +			name = Debug;
   2.278 +		};
   2.279 +		1DEB923708733DC60010E9CD /* Release */ = {
   2.280 +			isa = XCBuildConfiguration;
   2.281 +			buildSettings = {
   2.282 +				ARCHS = "$(ARCHS_STANDARD_32_BIT)";
   2.283 +				DEAD_CODE_STRIPPING = YES;
   2.284 +				GCC_C_LANGUAGE_STANDARD = c99;
   2.285 +				GCC_PRECOMPILE_PREFIX_HEADER = YES;
   2.286 +				GCC_PREPROCESSOR_DEFINITIONS = NDEBUG;
   2.287 +				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
   2.288 +				GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
   2.289 +				GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
   2.290 +				GCC_WARN_ABOUT_RETURN_TYPE = YES;
   2.291 +				GCC_WARN_EFFECTIVE_CPLUSPLUS_VIOLATIONS = NO;
   2.292 +				GCC_WARN_SHADOW = NO;
   2.293 +				GCC_WARN_UNINITIALIZED_AUTOS = YES;
   2.294 +				GCC_WARN_UNUSED_VARIABLE = YES;
   2.295 +				PREBINDING = NO;
   2.296 +				SDKROOT = macosx10.5;
   2.297 +				WARNING_CFLAGS = "-Wall";
   2.298 +			};
   2.299 +			name = Release;
   2.300 +		};
   2.301 +/* End XCBuildConfiguration section */
   2.302 +
   2.303 +/* Begin XCConfigurationList section */
   2.304 +		1DEB923108733DC60010E9CD /* Build configuration list for PBXNativeTarget "OttomanTest" */ = {
   2.305 +			isa = XCConfigurationList;
   2.306 +			buildConfigurations = (
   2.307 +				1DEB923208733DC60010E9CD /* Debug */,
   2.308 +				1DEB923308733DC60010E9CD /* Release */,
   2.309 +			);
   2.310 +			defaultConfigurationIsVisible = 0;
   2.311 +			defaultConfigurationName = Release;
   2.312 +		};
   2.313 +		1DEB923508733DC60010E9CD /* Build configuration list for PBXProject "Ottoman" */ = {
   2.314 +			isa = XCConfigurationList;
   2.315 +			buildConfigurations = (
   2.316 +				1DEB923608733DC60010E9CD /* Debug */,
   2.317 +				1DEB923708733DC60010E9CD /* Release */,
   2.318 +			);
   2.319 +			defaultConfigurationIsVisible = 0;
   2.320 +			defaultConfigurationName = Release;
   2.321 +		};
   2.322 +/* End XCConfigurationList section */
   2.323 +	};
   2.324 +	rootObject = 08FB7793FE84155DC02AAC07 /* Project object */;
   2.325 +}
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/include/Base.h	Sun Sep 20 15:14:12 2009 -0700
     3.3 @@ -0,0 +1,113 @@
     3.4 +/*
     3.5 + *  Base.h
     3.6 + *  Ottoman
     3.7 + *
     3.8 + *  Created by Jens Alfke on 8/18/09.
     3.9 + *  Copyright 2009 Jens Alfke. All rights reserved.
    3.10 + *  BSD-Licensed: See the file "LICENSE.txt" for details.
    3.11 + */
    3.12 +
    3.13 +#ifndef _MOOSEYARD_BASE_
    3.14 +#define _MOOSEYARD_BASE_
    3.15 +#include <stdlib.h>
    3.16 +#include <stdint.h>
    3.17 +#include <string.h>
    3.18 +#include <libkern/OSByteOrder.h>
    3.19 +#include <CoreFoundation/CFByteOrder.h>
    3.20 +
    3.21 +namespace Mooseyard {
    3.22 +    
    3.23 +    /** A 32-bit file position/offset. Supports files of up to 4GB. */
    3.24 +    typedef uint32_t FilePosition;
    3.25 +    const FilePosition kNoFilePosition = (FilePosition)-1L;
    3.26 +    
    3.27 +
    3.28 +    /** A simple structure representing a range of memory (a pointer plus a length).
    3.29 +        It doesn't do any memory management; it just points. */
    3.30 +    struct Blob {
    3.31 +        const void *bytes;
    3.32 +        size_t length;
    3.33 +        
    3.34 +        Blob()                                                  :bytes(NULL), length(0) { }
    3.35 +        Blob (const void *b, size_t len)                        :bytes(b), length(len) { }
    3.36 +        Blob (const char *str);
    3.37 +        const void* end() const                                 {return (const uint8_t*)bytes+length;}
    3.38 +        
    3.39 +        operator bool() const                                   {return bytes!=NULL;}
    3.40 +        bool operator== (const Blob &b)                         {return bytes==b.bytes && length==b.length;}
    3.41 +        bool equals (const Blob &b) const                       {return length==b.length && memcmp(bytes,b.bytes,length)==0;}
    3.42 +        void clear()                                            {bytes=NULL; length=0;}
    3.43 +    };
    3.44 +
    3.45 +    
    3.46 +    // Utility to offset a pointer by some number of bytes.
    3.47 +    inline const void* offsetBy(const void *src, size_t off)   {return (const uint8_t*)src + off;}
    3.48 +    inline void* offsetBy(void *src, size_t off)               {return (uint8_t*)src + off;}
    3.49 +    
    3.50 +    
    3.51 +    #define UNCOPYABLE(CLASS)  private: CLASS(const CLASS&);  CLASS& operator=(const CLASS&)
    3.52 +    
    3.53 +    
    3.54 +#pragma mark -
    3.55 +#pragma mark LITTLE-ENDIAN:
    3.56 +    
    3.57 +    /** A template for numeric types that are always stored in little-endian form.
    3.58 +        Useful for structures stored in files that need to be useable on all architectures. */
    3.59 +    template <typename INT, typename REP=INT>
    3.60 +    class LittleEndian {
    3.61 +    public:
    3.62 +        inline LittleEndian ();
    3.63 +        inline LittleEndian (INT);
    3.64 +        inline operator INT() const;
    3.65 +        inline LittleEndian& operator= (INT);
    3.66 +        inline LittleEndian& operator= (const LittleEndian&);
    3.67 +        inline LittleEndian& operator++();
    3.68 +        inline LittleEndian& operator--();
    3.69 +    private:
    3.70 +        static inline REP makeLittle (INT);
    3.71 +        static inline INT makeNative (REP);
    3.72 +        REP _value;
    3.73 +    };
    3.74 +    
    3.75 +    typedef LittleEndian<double,CFSwappedFloat64> LittleEndianDouble;
    3.76 +    
    3.77 +
    3.78 +    // Implementation gunk:
    3.79 +    template <typename INT, typename REP>
    3.80 +    inline LittleEndian<INT,REP>::LittleEndian ()               :_value() { }
    3.81 +    template <typename INT, typename REP>
    3.82 +    inline LittleEndian<INT,REP>::LittleEndian (INT i)          :_value(makeLittle(i)) { }
    3.83 +    template <typename INT, typename REP>
    3.84 +    inline LittleEndian<INT,REP>::operator INT() const          {return makeNative(_value);}
    3.85 +    template <typename INT, typename REP>
    3.86 +    inline LittleEndian<INT,REP>& LittleEndian<INT,REP>::operator++() {return *this = (INT)*this + 1;}
    3.87 +    template <typename INT, typename REP>
    3.88 +    inline LittleEndian<INT,REP>& LittleEndian<INT,REP>::operator--() {return *this = (INT)*this - 1;}
    3.89 +    
    3.90 +    template <typename INT, typename REP>
    3.91 +    inline LittleEndian<INT,REP>& LittleEndian<INT,REP>::operator= (INT i) {
    3.92 +        _value = makeLittle(i);
    3.93 +        return *this;
    3.94 +    }
    3.95 +    template <typename INT, typename REP>
    3.96 +    inline LittleEndian<INT,REP>& LittleEndian<INT,REP>::operator= (const LittleEndian<INT,REP> &p) {
    3.97 +        _value = p._value;
    3.98 +        return *this;
    3.99 +    }
   3.100 +    
   3.101 +    template <> inline uint32_t LittleEndian<uint32_t>::makeLittle (uint32_t i) 
   3.102 +    {return OSSwapHostToLittleInt32(i);}
   3.103 +    template <> inline uint32_t LittleEndian<uint32_t>::makeNative (uint32_t i) 
   3.104 +    {return OSSwapLittleToHostInt32(i);}
   3.105 +    template <> inline uint16_t LittleEndian<uint16_t>::makeLittle (uint16_t i) 
   3.106 +    {return OSSwapHostToLittleInt16(i);}
   3.107 +    template <> inline uint16_t LittleEndian<uint16_t>::makeNative (uint16_t i) 
   3.108 +    {return OSSwapLittleToHostInt16(i);}
   3.109 +    template <> inline CFSwappedFloat64 LittleEndian<double,CFSwappedFloat64>::makeLittle (double d) 
   3.110 +    {return CFConvertDoubleHostToSwapped(d);}
   3.111 +    template <> inline double LittleEndian<double,CFSwappedFloat64>::makeNative (CFSwappedFloat64 d) 
   3.112 +    {return CFConvertDoubleSwappedToHost(d);}
   3.113 +    
   3.114 +}
   3.115 +
   3.116 +#endif _MOOSEYARD_BASE_
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/include/Chunk.h	Sun Sep 20 15:14:12 2009 -0700
     4.3 @@ -0,0 +1,97 @@
     4.4 +/*
     4.5 + *  Chunk.h
     4.6 + *  Ottoman
     4.7 + *
     4.8 + *  Created by Jens Alfke on 8/27/09.
     4.9 + *  Copyright 2009 Jens Alfke. All rights reserved.
    4.10 + *  BSD-Licensed: See the file "LICENSE.txt" for details.
    4.11 + */
    4.12 +
    4.13 +#ifndef _MOOSEYARD_CHUNK_
    4.14 +#define _MOOSEYARD_CHUNK_
    4.15 +#include "File.h"
    4.16 +
    4.17 +namespace Mooseyard {
    4.18 +    
    4.19 +    class KeyValueChunk;
    4.20 +    
    4.21 +#pragma pack(2)
    4.22 +    
    4.23 +    /** An item stored in a file. It's prefixed with its length, so that the entire file
    4.24 +        can be scanned for chunks easily. */
    4.25 +    class Chunk {
    4.26 +    public:
    4.27 +        uint32_t size() const                       {return _size;}
    4.28 +        uint16_t type() const;
    4.29 +        
    4.30 +        /** If this is a KeyValueChunk, returns itself cast to that type, else NULL. */
    4.31 +        const KeyValueChunk* asKeyValue() const;
    4.32 +        
    4.33 +        /** Write a Chunk to the file, in pieces a la writev. */
    4.34 +        static size_t writeMultiple (File *file, uint16_t type,
    4.35 +                                     Blob blobs[], int count)  throw(File::Error);
    4.36 +        
    4.37 +        static size_t writePadding (File *file);
    4.38 +
    4.39 +        static const uint16_t kChunkTypePadding = 0;
    4.40 +
    4.41 +        const void* end() const                     {return offsetBy(this,_size);}
    4.42 +
    4.43 +    protected:
    4.44 +        Chunk (uint32_t size, uint16_t type)        :_size(size), _keyLength(0xFFFF), _type(type) { }
    4.45 +
    4.46 +    private:
    4.47 +        // This is mapped to data in the file! Don't mess with it!
    4.48 +        LittleEndian<uint32_t> _size;
    4.49 +        LittleEndian<uint16_t> _keyLength;          // Used by KeyValueChunk; else 0xFFFF
    4.50 +        LittleEndian<uint16_t> _type;               // Not used by KeyValueChunk
    4.51 +        friend class KeyValueChunk;
    4.52 +    };
    4.53 +    
    4.54 +    
    4.55 +    /** A key/value pair as stored in the memory-mapped file. */
    4.56 +    class KeyValueChunk :public Chunk {
    4.57 +    public:
    4.58 +        Blob key() const;
    4.59 +        Blob value() const;
    4.60 +        
    4.61 +        bool hasKey (Blob key) const;
    4.62 +        
    4.63 +        void validate() const  throw(File::Error);
    4.64 +        
    4.65 +        /** Write a KeyValueChunk to a file. */
    4.66 +        static size_t write (File* file, FilePosition pos, Blob key, Blob value)  throw(File::Error);
    4.67 +
    4.68 +        static const uint16_t kChunkType = 1;
    4.69 +
    4.70 +    private:
    4.71 +        const char* valuePtr() const;
    4.72 +    };    
    4.73 +   
    4.74 +#pragma options align=reset
    4.75 +    
    4.76 +    
    4.77 +    /** An iterator over all the Chunks in a file. */
    4.78 +    class ChunkIterator {
    4.79 +    public:
    4.80 +        ChunkIterator (File*, FilePosition start);
    4.81 +        
    4.82 +        const Chunk* chunk() const              {return _chunk;}
    4.83 +        FilePosition position() const           {return _pos;}
    4.84 +        bool atEOF() const;
    4.85 +        bool next();
    4.86 +        
    4.87 +        operator const Chunk*() const           {return _chunk;}
    4.88 +        virtual bool hasValue() const           {return _chunk != NULL;}
    4.89 +        operator bool() const                   {return hasValue();}
    4.90 +        ChunkIterator& operator++()             {next(); return *this;}
    4.91 +    private:
    4.92 +        void _loadChunk();
    4.93 +        File *_file;
    4.94 +        FilePosition _pos, _length;
    4.95 +        const Chunk* _chunk;
    4.96 +    };
    4.97 +    
    4.98 +}
    4.99 +
   4.100 +#endif _MOOSEYARD_CHUNK_
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/include/Dictionary.h	Sun Sep 20 15:14:12 2009 -0700
     5.3 @@ -0,0 +1,208 @@
     5.4 +/*
     5.5 + *  Dictionary.h
     5.6 + *  Ottoman
     5.7 + *
     5.8 + *  Created by Jens Alfke on 8/23/09.
     5.9 + *  Copyright 2009 Jens Alfke. All rights reserved.
    5.10 + *  BSD-Licensed: See the file "LICENSE.txt" for details.
    5.11 + */
    5.12 +
    5.13 +#ifndef _MOOSEYARD_DICTIONARY_
    5.14 +#define _MOOSEYARD_DICTIONARY_
    5.15 +#include "Base.h"
    5.16 +
    5.17 +namespace Mooseyard {
    5.18 +
    5.19 +    typedef uint32_t HashCode;
    5.20 +    
    5.21 +    /** A Key is a data blob treated as a dictionary key.
    5.22 +        It carries its hash-code around with it, to avoid redundant computation. */
    5.23 +    struct Key :public Blob {
    5.24 +        HashCode hash;
    5.25 +        
    5.26 +        Key ()                                          :Blob(), hash(0) { }
    5.27 +        explicit Key (const Blob &b)                    :Blob(b), hash(computeHash(b)) { }
    5.28 +        Key (const Blob &b, HashCode h)                 :Blob(b), hash(h) { }
    5.29 +        Key (const char *str)                           :Blob(str), hash(computeHash(*this)) { }
    5.30 +        Key (const void *b, uint32_t len, HashCode h)   :Blob(b,len), hash(h) { }
    5.31 +
    5.32 +        static HashCode computeHash (Blob);
    5.33 +    };
    5.34 +    
    5.35 +    
    5.36 +    /** An abstract interface for key/value dictionaries.
    5.37 +        Dictionary itself is read-only; its subclass MutableDictionary allows changes. */
    5.38 +    class Dictionary {
    5.39 +    public:
    5.40 +        class Iterator;
    5.41 +        class ChangeIterator;
    5.42 +
    5.43 +        virtual ~Dictionary();
    5.44 +        
    5.45 +        /** The number of items in the dictionary. */
    5.46 +        virtual int count() const =0;
    5.47 +        
    5.48 +        /** Returns the value associated with the given key.
    5.49 +            Keys are compared by exact binary data equality.
    5.50 +            The data pointed to by the value returned is valid at least until the next call to
    5.51 +            this Dictionary; some subclasses may guarantee a longer lifetime.
    5.52 +            If there is no value for the given key, a Blob of {NULL, 0} will be returned. */
    5.53 +        virtual Blob get (Key) const =0;
    5.54 +        
    5.55 +        /** Returns true iff there is a value associated with the given key. */
    5.56 +        virtual bool contains (Key key) const      {return get(key).bytes != NULL;}
    5.57 +        
    5.58 +        /** Returns a pointer to a new Iterator object that can be used to iterate this Dictionary.
    5.59 +            The iterator must be deleted when you're done with it. */
    5.60 +        virtual Iterator* iterate() const =0;
    5.61 +        
    5.62 +        /** A singleton empty Dictionary. */
    5.63 +        static const Dictionary& kEmpty;
    5.64 +        
    5.65 +        // utilities
    5.66 +        /** Returns a suggested size for a hash table that can hold 'capacity' items without
    5.67 +            being more than the 'load' fraction full; result is always a power of two. */
    5.68 +        static int choosePowerOfTwoSize (int capacity, float load = kMaxLoadFactor);
    5.69 +
    5.70 +        static int chooseAnyOldSize (int capacity, float load = kMaxLoadFactor);
    5.71 +
    5.72 +        static const int   kMinSize;
    5.73 +        static const float kMinLoadFactor;
    5.74 +        static const float kMaxLoadFactor;
    5.75 +        
    5.76 +    protected:
    5.77 +        Dictionary();
    5.78 +    };
    5.79 +    
    5.80 +    
    5.81 +    /** An abstract interface for mutable key/value dictionaries; extends Dictionary. */
    5.82 +    class MutableDictionary : public Dictionary {
    5.83 +    public:
    5.84 +        /** Stores the given value for the given key, replacing any prior value.
    5.85 +            The memory for the key and value is copied, so you don't need to preserve it after
    5.86 +            the call returns. */
    5.87 +        virtual void put (Key, Blob value) =0;
    5.88 +        
    5.89 +        /** Adds the contents of the given Dictionary to this one.
    5.90 +            Effectively calls this->put() with every key/value pair in the source Dictionary. */
    5.91 +        virtual void add (Dictionary*);
    5.92 +        
    5.93 +        /** Removes the value for the given key, if any.
    5.94 +            Returns true if the value was removed, false if there was no value. */
    5.95 +        virtual bool remove (Key) =0;
    5.96 +        
    5.97 +        /** Removes all keys and values, returning the dictionary to an empty state. */
    5.98 +        virtual void removeAll() = 0;
    5.99 +    };
   5.100 +    
   5.101 +    
   5.102 +    /** Abstract iterator interface. Each Dictionary subclass defines its own.
   5.103 +        The order in which items are returned is undefined, and in effect quite random, due to
   5.104 +        the properties of hash tables. */
   5.105 +    class Dictionary::Iterator {
   5.106 +    public:
   5.107 +        virtual ~Iterator();
   5.108 +        
   5.109 +        /** The current key the iterator is pointing to. */
   5.110 +        virtual Key key() const =0;
   5.111 +        /** The current value the iterator is pointing to. */
   5.112 +        virtual Blob value() const =0;
   5.113 +        /** Returns true if the iterator has a current item, or false if it's reached the end. */
   5.114 +        virtual bool hasValue() const           {return key().bytes != 0;}
   5.115 +        /** Advances the Iterator to the next item. Returns false if it's reached the end.
   5.116 +            (Note that the order of items is undefined and in practice quite random.) */
   5.117 +        virtual bool next() =0;
   5.118 +        
   5.119 +        operator bool() const                   {return hasValue();}
   5.120 +        Iterator& operator++()                  {next(); return *this;}
   5.121 +    };
   5.122 +    
   5.123 +    class OverlayDictionary;
   5.124 +    
   5.125 +    
   5.126 +    /** An iterator that walks through the differences between two Dictionaries. */
   5.127 +    class Dictionary::ChangeIterator :public Dictionary::Iterator {
   5.128 +    public:
   5.129 +        ChangeIterator (const Dictionary *dict1, const Dictionary *dict2);
   5.130 +        explicit ChangeIterator (const OverlayDictionary*);
   5.131 +        virtual ~ChangeIterator();
   5.132 +        
   5.133 +        /** Returns the current key, whose values in the two dictionaries differ. */
   5.134 +        virtual Key key() const                 {return _iter->key();}
   5.135 +        /** Returns the corresponding value from dict1.
   5.136 +            May be empty (NULL,0) if the key does not appear in dict1 (i.e. inserted into dict2.) */
   5.137 +        virtual Blob value() const              {return _iter->value();}
   5.138 +        /** Returns the corresponding value from dict2.
   5.139 +            May be empty (NULL,0) if the key does not appear in dict2 (i.e. deleted from dict2.) */
   5.140 +        virtual Blob otherValue() const         {return _otherValue;}
   5.141 +        virtual bool hasValue() const           {return _iter->hasValue();}
   5.142 +        virtual bool next();
   5.143 +        
   5.144 +    private:
   5.145 +        void _init (const Dictionary *dict1, const Dictionary *dict2);
   5.146 +        bool _skipMatching();
   5.147 +        const Dictionary *_dict2;
   5.148 +        Iterator *_iter;
   5.149 +        Blob _otherValue;
   5.150 +    };
   5.151 +    
   5.152 +    
   5.153 +    /** Adapter class that allows a Dictionary to appear to be mutable, by using a separate
   5.154 +        mutable dictionary to remember the changes. */
   5.155 +    class OverlayDictionary :public MutableDictionary {
   5.156 +    public:
   5.157 +        /** Creates a mutable dictionary that overlays base. Initially its contents will appear
   5.158 +            identical to base. It can be changed, but the changes will not affect the base. */
   5.159 +        explicit OverlayDictionary (const Dictionary *base);
   5.160 +        virtual ~OverlayDictionary();
   5.161 +        
   5.162 +        /** The base dictionary this is overlaying. */
   5.163 +        const Dictionary* base() const              {return _base;}
   5.164 +        /** The dictionary that contains the changes made to the base.
   5.165 +            Only keys/values that have been changed appear here. */
   5.166 +        const Dictionary* overlay() const           {return _overlay;}
   5.167 +        /** Returns true if changes have been made. */
   5.168 +        bool isChanged() const;
   5.169 +        /** Returns true if the base has been completely replaced by calling removeAll. */
   5.170 +        bool baseReplaced() const                   {return _allRemoved;}
   5.171 +        
   5.172 +        /** Removes all changes, restoring the overlay to look identical to the base. */
   5.173 +        void revert();
   5.174 +        /** Changes the overlay to a new base, clearing all changes. */
   5.175 +        void revertTo (const Dictionary* newBase);
   5.176 +        
   5.177 +        /** Changes the base without clearing changes. */
   5.178 +        void rebase (const Dictionary* newBase);
   5.179 +        
   5.180 +        virtual int count() const;
   5.181 +        virtual Blob get (Key) const;
   5.182 +        virtual bool contains (Key) const;
   5.183 +        virtual void put (Key, Blob value);
   5.184 +        virtual bool remove (Key);
   5.185 +        virtual void removeAll();
   5.186 +        virtual Iterator* iterate() const           {return new Iterator(*this);}
   5.187 +        
   5.188 +        class Iterator :public Dictionary::Iterator {
   5.189 +        public:
   5.190 +            explicit Iterator (const OverlayDictionary&);
   5.191 +            virtual Key key() const;
   5.192 +            virtual Blob value() const;
   5.193 +            virtual bool next();
   5.194 +        private:
   5.195 +            bool skipCurrentState();
   5.196 +            Dictionary::Iterator *_iter;
   5.197 +            const MutableDictionary *_overlay;
   5.198 +        };
   5.199 +        
   5.200 +    private:
   5.201 +        const Dictionary *_base;
   5.202 +        MutableDictionary *_overlay;
   5.203 +        int _count;
   5.204 +        bool _allRemoved;
   5.205 +        friend class ChangeIterator;
   5.206 +    };
   5.207 +    
   5.208 +    
   5.209 +}
   5.210 +
   5.211 +#endif _MOOSEYARD_DICTIONARY_
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/include/File.h	Sun Sep 20 15:14:12 2009 -0700
     6.3 @@ -0,0 +1,107 @@
     6.4 +/*
     6.5 + *  File.h
     6.6 + *  Ottoman
     6.7 + *
     6.8 + *  Created by Jens Alfke on 8/20/09.
     6.9 + *  Copyright 2009 Jens Alfke. All rights reserved.
    6.10 + *  BSD-Licensed: See the file "LICENSE.txt" for details.
    6.11 + */
    6.12 +
    6.13 +#ifndef _MOOSEYARD_FILE_
    6.14 +#define _MOOSEYARD_FILE_
    6.15 +#include "Base.h"
    6.16 +
    6.17 +namespace Mooseyard {
    6.18 +
    6.19 +struct Blob;
    6.20 +class MemoryMap;
    6.21 +
    6.22 +/** An open file on disk; a thin wrapper around a Unix file descriptor. */
    6.23 +class File {
    6.24 +public:
    6.25 +    
    6.26 +    struct Error {
    6.27 +        const int code;
    6.28 +        const char* const message;
    6.29 +        
    6.30 +        Error(int c, const char *m)                                 :code(c), message(m) { }
    6.31 +        Error(const char *m);
    6.32 +    };
    6.33 +    static const int kEOF = 10001;
    6.34 +    
    6.35 +    
    6.36 +    File (const char *filename, bool writeable =false, bool create =false)  throw(Error);
    6.37 +    File (const char *filename, int oflag)  throw(Error);
    6.38 +    ~File();
    6.39 +    
    6.40 +    off_t length() const  throw(Error);
    6.41 +    void setLength (off_t)  throw(Error);
    6.42 +
    6.43 +    off_t position() const  throw(Error);
    6.44 +    void setPosition (off_t)  throw(Error);
    6.45 +    off_t setPositionToEnd (off_t bytesBefore =0)  throw(Error);
    6.46 +    
    6.47 +    void read (void *dst, size_t)  throw(Error);
    6.48 +    size_t write (const void *src, size_t)  throw(Error);
    6.49 +
    6.50 +    void readFrom (off_t where, void *dst, size_t)  throw(Error);
    6.51 +    void writeTo (off_t where, const void *src, size_t)  throw(Error);
    6.52 +    
    6.53 +    size_t writeMultiple (Blob blobs[], int count)  throw(Error);
    6.54 +    size_t writePadding (int alignment);  // alignment should be a power of 2
    6.55 +    
    6.56 +    MemoryMap* map();
    6.57 +    const MemoryMap* map() const                            {return const_cast<File*>(this)->map();}
    6.58 +    void mapRegion (off_t position, size_t length);
    6.59 +    const void* mappedPosition (off_t position) const;
    6.60 +    
    6.61 +    void flush()  throw(Error);             // Regular fsync call
    6.62 +    void flushDisk()  throw(Error);         // Expensive F_FULLFSYNC call
    6.63 +    
    6.64 +    template <typename T>
    6.65 +    void read (T& t)  throw(Error)                              {read(&t,sizeof(t));}
    6.66 +    template <typename T>
    6.67 +    size_t write (const T& t)  throw(Error)                     {return write(&t,sizeof(t));}
    6.68 +    template <typename T>
    6.69 +    void readFrom (off_t where, T &t)  throw(Error)             {readFrom(where,&t,sizeof(t));}
    6.70 +    template <typename T>
    6.71 +    void writeTo (off_t where, const T &t)  throw(Error)        {writeTo(where,&t,sizeof(t));}
    6.72 +    
    6.73 +    bool hasPath (const char *path) const  throw(Error);
    6.74 +
    6.75 +    static void unlink (const char *filename)  throw(Error);
    6.76 +    static void rename (const char *srcFilename, const char *dstFilename)  throw(Error);
    6.77 +    
    6.78 +    
    6.79 +    class Lock {
    6.80 +    public:
    6.81 +        Lock (File*, bool block =true)  throw(Error);
    6.82 +        ~Lock();
    6.83 +        bool isLocked() const                   {return _locked;}
    6.84 +        operator bool() const                   {return _locked;}
    6.85 +        bool retry();
    6.86 +    private:
    6.87 +        UNCOPYABLE(Lock);
    6.88 +        File *_file;
    6.89 +        int _mode;
    6.90 +        bool _locked;
    6.91 +    };
    6.92 +    
    6.93 +private:
    6.94 +    static int _open (const char *filename, int posixMode)  throw(Error);
    6.95 +    static int _check (int result)  throw(Error);
    6.96 +    static void _checkRead (ssize_t result, size_t expectedSize)  throw(Error);
    6.97 +    bool _lock (bool block);
    6.98 +    void _unlock();
    6.99 +    UNCOPYABLE(File);
   6.100 +    
   6.101 +    const int _fd;
   6.102 +    mutable MemoryMap *_memoryMap;
   6.103 +    int _locked;
   6.104 +    friend class Lock;
   6.105 +    friend class MemoryMap;
   6.106 +};
   6.107 +
   6.108 +}
   6.109 +
   6.110 +#endif _MOOSEYARD_FILE_
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/include/Hash.h	Sun Sep 20 15:14:12 2009 -0700
     7.3 @@ -0,0 +1,119 @@
     7.4 +/*
     7.5 + *  Hash.h
     7.6 + *  Ottoman
     7.7 + *
     7.8 + *  Created by Jens Alfke on 8/20/09.
     7.9 + *  Copyright 2009 Jens Alfke. All rights reserved.
    7.10 + *  BSD-Licensed: See the file "LICENSE.txt" for details.
    7.11 + */
    7.12 +
    7.13 +#ifndef _MOOSEYARD_HASH_
    7.14 +#define _MOOSEYARD_HASH_
    7.15 +#include "Base.h"
    7.16 +#include "Dictionary.h"
    7.17 +
    7.18 +namespace Mooseyard {
    7.19 +        
    7.20 +    /** A fairly low-level hash-table class whose keys and values are arbitrary data blobs.
    7.21 +        This class does no memory management of the keys and values: the memory ranges
    7.22 +        pointed to by the key and value parameters to put() must remain valid as long as
    7.23 +        that key or value remains in the hash-table. */
    7.24 +    class Hash {
    7.25 +        
    7.26 +    public:
    7.27 +        typedef void* Value;
    7.28 +        
    7.29 +        /** Creates a new, empty Hash. */
    7.30 +        explicit Hash (int capacity = 50);
    7.31 +        
    7.32 +        ~Hash();
    7.33 +        
    7.34 +        /** Gets the value associated with the given key. */
    7.35 +        Value get(Key) const;
    7.36 +        Value operator[] (Key key) const                        {return get(key);}
    7.37 +        
    7.38 +        /** Returns the number of values in the Hash. */
    7.39 +        int count() const                                       {return _count;}
    7.40 +        
    7.41 +        /** Sets the given value for the given key, replacing any existing value. */
    7.42 +        void put(Key, Value);
    7.43 +        
    7.44 +        /** Removes the value with the given key. */
    7.45 +        bool remove(Key);
    7.46 +        
    7.47 +        /** Removes all values. */
    7.48 +        void removeAll();
    7.49 +        
    7.50 +        void dump() const;
    7.51 +        
    7.52 +        class IndexEntry;
    7.53 +        
    7.54 +        class Iterator {
    7.55 +        public:
    7.56 +            Iterator (const Hash *h);
    7.57 +            Iterator (const Hash &h);
    7.58 +            operator bool() const                           {return hasValue();}
    7.59 +            Value operator*() const                         {return value();}
    7.60 +            Iterator& operator++()                          {next(); return *this;}
    7.61 +            
    7.62 +            Key key() const;
    7.63 +            Value value() const;
    7.64 +            bool hasValue() const                           {return _entry < _end;}
    7.65 +            bool next();
    7.66 +        private:
    7.67 +            Iterator (IndexEntry *start, IndexEntry*end);
    7.68 +            IndexEntry* _entry;
    7.69 +            IndexEntry* const _end;
    7.70 +            UNCOPYABLE(Iterator);
    7.71 +        };
    7.72 +        
    7.73 +    private:
    7.74 +        IndexEntry& _find (Key key) const;
    7.75 +        IndexEntry& _findForPut (const IndexEntry &newEntry);
    7.76 +        void _resize(int newSize);
    7.77 +        void _read();
    7.78 +        UNCOPYABLE(Hash);
    7.79 +        
    7.80 +        int _count;
    7.81 +        int _tableSize;
    7.82 +        IndexEntry *_table;
    7.83 +        
    7.84 +        friend class Iterator;
    7.85 +    };
    7.86 +    
    7.87 +
    7.88 +
    7.89 +    /** A concrete Dictionary implemented with a Hash. */
    7.90 +    class HashDictionary :public MutableDictionary {
    7.91 +    public:
    7.92 +        explicit HashDictionary (int capacity =50)  :_hash(capacity) { }
    7.93 +        virtual ~HashDictionary();
    7.94 +        virtual int count() const                   {return _hash.count();}
    7.95 +        virtual bool contains (Key) const;
    7.96 +        virtual Blob get (Key key) const            {return _convertValue(_hash.get(key));}
    7.97 +        virtual void put (Key, Blob value);
    7.98 +        virtual bool remove (Key);
    7.99 +        virtual void removeAll();
   7.100 +        virtual Iterator* iterate() const           {return new Iterator(*this);}
   7.101 +        
   7.102 +        class Iterator :public Dictionary::Iterator {
   7.103 +        public:
   7.104 +            explicit Iterator (const HashDictionary &d)      :_it(d._hash) { }
   7.105 +            virtual Key key() const                 {return _it.key();}
   7.106 +            virtual Blob value() const              {return _convertValue(_it.value());}
   7.107 +            virtual bool hasValue() const           {return _it.hasValue();}
   7.108 +            virtual bool next()                     {return _it.next();}
   7.109 +        private:
   7.110 +            Hash::Iterator _it;
   7.111 +        };
   7.112 +        
   7.113 +    private:
   7.114 +        static Blob _convertValue (void*);
   7.115 +        Hash _hash;
   7.116 +        friend class Iterator;
   7.117 +    };
   7.118 +    
   7.119 +    
   7.120 +}
   7.121 +
   7.122 +#endif _MOOSEYARD_HASH_
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/include/Index.h	Sun Sep 20 15:14:12 2009 -0700
     8.3 @@ -0,0 +1,76 @@
     8.4 +/*
     8.5 + *  Index.h
     8.6 + *  Ottoman
     8.7 + *
     8.8 + *  Created by Jens Alfke on 8/27/09.
     8.9 + *  Copyright 2009 Jens Alfke. All rights reserved.
    8.10 + *  BSD-Licensed: See the file "LICENSE.txt" for details.
    8.11 + */
    8.12 +
    8.13 +#ifndef _MOOSEYARD_INDEX_
    8.14 +#define _MOOSEYARD_INDEX_
    8.15 +#include "Chunk.h"
    8.16 +#include "Dictionary.h"
    8.17 +
    8.18 +namespace Mooseyard {
    8.19 +    
    8.20 +    class KeyValueChunk;
    8.21 +    
    8.22 +    /** An in-file hash table. This structure is stored directly in the file, memory-mapped.
    8.23 +        Index is normally only used internally by VersionDictionary. */
    8.24 +    class Index :public Chunk {
    8.25 +    public:
    8.26 +        
    8.27 +        class Entry;
    8.28 +        
    8.29 +        static const Index* indexMappedAt (const void*);
    8.30 +        static Index* create (int capacity);
    8.31 +        
    8.32 +        int count() const                                   {return _count;}
    8.33 +        
    8.34 +        Blob get (File *file, Key) const;
    8.35 +        bool put (File *file, Key, FilePosition valuePosition, FilePosition maxPosition);
    8.36 +        bool remove (File *file, Key, FilePosition maxPosition);
    8.37 +        void removeAll();
    8.38 +        
    8.39 +        void copyFrom (File *file, const Index *index);
    8.40 +        
    8.41 +        class Iterator;
    8.42 +        
    8.43 +        void validate() const  throw(File::Error);
    8.44 +        void validateEntries (const File *file) const  throw(File::Error);
    8.45 +        static const uint16_t kChunkType = 2;
    8.46 +        
    8.47 +    private:
    8.48 +        Index (uint32_t tableSize);
    8.49 +        const Entry* table (int index) const;
    8.50 +        Entry* table (int index);
    8.51 +        const KeyValueChunk* _find (File *file, Key) const;
    8.52 +        bool _put (File *file, Key, FilePosition, FilePosition maxPosition);
    8.53 +
    8.54 +        // This is mapped to data in the file! Don't mess with it!
    8.55 +        LittleEndian<uint32_t>  _magicNumber;
    8.56 +        LittleEndian<uint32_t>  _count;
    8.57 +        LittleEndian<uint32_t>  _tableSize;
    8.58 +        char                    _table[0];      // Actually Entry[]
    8.59 +    };
    8.60 +  
    8.61 +    
    8.62 +    
    8.63 +    class Index::Iterator :public Dictionary::Iterator {
    8.64 +    public:
    8.65 +        Iterator (File*, const Index*);
    8.66 +        virtual Key key() const;
    8.67 +        virtual Blob value() const;
    8.68 +        virtual bool next();
    8.69 +        virtual bool hasValue() const;
    8.70 +        
    8.71 +        FilePosition valuePosition();
    8.72 +    private:
    8.73 +        File* const _file;
    8.74 +        const Index::Entry *_current, *_end;
    8.75 +    };
    8.76 +    
    8.77 +}
    8.78 +
    8.79 +#endif //_MOOSEYARD_INDEX_
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/include/MemoryMap.h	Sun Sep 20 15:14:12 2009 -0700
     9.3 @@ -0,0 +1,53 @@
     9.4 +/*
     9.5 + *  MemoryMap.h
     9.6 + *  Ottoman
     9.7 + *
     9.8 + *  Created by Jens Alfke on 9/17/09.
     9.9 + *  Copyright 2009 Jens Alfke. All rights reserved.
    9.10 + *  BSD-Licensed: See the file "LICENSE.txt" for details.
    9.11 + */
    9.12 +
    9.13 +#include "File.h"
    9.14 +
    9.15 +namespace Mooseyard {
    9.16 +    
    9.17 +    /** A flexible memory-map on a file, which can handle multiple discontiguous mapped regions.
    9.18 +        Clients usually access this functionality through File, which delegates to it. */
    9.19 +    class MemoryMap {
    9.20 +    public:
    9.21 +        MemoryMap (File* file)                          :_file(file), _nRegions(0), _regions(NULL) { }
    9.22 +        ~MemoryMap();
    9.23 +        
    9.24 +        void mapRegion (off_t position, size_t length);
    9.25 +        const void* mappedPosition (off_t position) const;
    9.26 +        
    9.27 +    private:
    9.28 +        UNCOPYABLE(MemoryMap);
    9.29 +        class Region;
    9.30 +        const void* _at (off_t) const;
    9.31 +        
    9.32 +        File *_file;
    9.33 +        int _nRegions;
    9.34 +        Region **_regions;
    9.35 +    };
    9.36 +    
    9.37 +    
    9.38 +    class MemoryMap::Region {
    9.39 +    public:
    9.40 +        Region (File*, off_t position, size_t length);
    9.41 +        ~Region();
    9.42 +        off_t position() const                  {return _position;}
    9.43 +        size_t length() const                   {return _length;}
    9.44 +        off_t end() const                       {return _position + _length;}
    9.45 +        bool setLength (size_t);          // Returns false if it failed
    9.46 +        const void* mappedPosition(off_t);
    9.47 +    private:
    9.48 +        UNCOPYABLE(Region);
    9.49 +        File* const _file;
    9.50 +        const off_t _position;
    9.51 +        size_t _length;
    9.52 +        const uint8_t *_start;
    9.53 +    };
    9.54 +    
    9.55 +    
    9.56 +}
    10.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.2 +++ b/include/Ottoman.h	Sun Sep 20 15:14:12 2009 -0700
    10.3 @@ -0,0 +1,99 @@
    10.4 +/*
    10.5 + *  Ottoman.h
    10.6 + *  Ottoman
    10.7 + *
    10.8 + *  Created by Jens Alfke on 8/31/09.
    10.9 + *  Copyright 2009 Jens Alfke. All rights reserved.
   10.10 + *  BSD-Licensed: See the file "LICENSE.txt" for details.
   10.11 + */
   10.12 +
   10.13 +#ifndef _MOOSEYARD_OTTOMAN_
   10.14 +#define _MOOSEYARD_OTTOMAN_
   10.15 +
   10.16 +namespace Mooseyard {
   10.17 +    
   10.18 +    class ChunkIterator;
   10.19 +    class Dictionary;
   10.20 +    class File;
   10.21 +    class VersionDictionary;
   10.22 +    class OverlayDictionary;
   10.23 +    
   10.24 +    
   10.25 +    /** A version-controlled persistent dictionary. 
   10.26 +        Each version is stored as a VersionDictionary, unsaved changes as an OverlayDictionary. */
   10.27 +    class Ottoman {
   10.28 +    public:
   10.29 +        /** Creates an "untitled" Ottoman with no file and no lastVersion.
   10.30 +            After adding values to the currentVersion, use saveAs to save it. */
   10.31 +        Ottoman ();
   10.32 +        
   10.33 +        /** Opens an existing Ottoman file. */
   10.34 +        explicit Ottoman (const char *filename, bool writeable =true);
   10.35 +        
   10.36 +        /** Closes an Ottoman. */
   10.37 +        virtual ~Ottoman();
   10.38 +        
   10.39 +        /** The current filename, or NULL if the receiver is still in the "untitled" state. */
   10.40 +        virtual const char* filename() const                {return _filename;}
   10.41 +        
   10.42 +        /** The latest saved version of the dictionary. 
   10.43 +            Earlier versions can be accessed through its previousVersion() accessor.
   10.44 +            This will be NULL if the receiver is still in the "untitled" state. */
   10.45 +        virtual VersionDictionary* lastVersion() const         {return _lastVersion;}
   10.46 +        
   10.47 +        /** A mutable overlay representing the current state of the dictionary.
   10.48 +            Changes are made in memory until save() is called.
   10.49 +            This will be NULL if the receiver was opened read-only (writeable=false). */
   10.50 +        virtual OverlayDictionary* currentVersion() const   {return _current;}
   10.51 +        
   10.52 +        /** Has the on-disk file been updated with newer revisions than what I know about? */
   10.53 +        virtual bool needsSync() const;
   10.54 +        
   10.55 +        /** Reads any newer versions from disk.
   10.56 +            Returns true if new versions were read, false if there were none.
   10.57 +            Afterwards, lastVersion() will return the latest version in the file. 
   10.58 +            (The old lastVersion dictionary is still around, so you can save a pointer to it 
   10.59 +            before the call and use it later to see what changed.)
   10.60 +            Changes made to the currentVersion dictionary are not lost, but are now relative
   10.61 +            to the new lastVersion. You may want to scan them and resolve conflicts. */
   10.62 +        virtual bool sync();
   10.63 +        
   10.64 +        /** Saves the current version to the file, by appending.
   10.65 +            Returns false if there is a version conflict; in that case you need to call sync(),
   10.66 +            possibly resolve conflicts, and then call save() again. */
   10.67 +        virtual bool save();
   10.68 +        
   10.69 +        /** Saves the current version to the file, by writing to a new temporary file and
   10.70 +            then atomically replacing the original. 
   10.71 +            Older versions, and older copies of the data, are not preserved, so this
   10.72 +            will typically shrink the file quite a bit.
   10.73 +            Returns false if there is a version conflict. */
   10.74 +        virtual bool saveAndCompact();
   10.75 +         
   10.76 +        /** Saves the current version to a new file, leaving the new file open, 
   10.77 +            so subsequent saves will be written to it. */
   10.78 +        virtual void saveAs (const char *newFilename, bool overwriteAllowed =true);
   10.79 +        
   10.80 +        /** Scans the file for damage. Returns true if the file is OK, false if problems are found.
   10.81 +            If the 'repair' flag is set, will truncate the file to the last valid version. */
   10.82 +        bool scavenge (bool repair =false);
   10.83 +        
   10.84 +        ChunkIterator* chunkIterator() const;       // for testing purposes
   10.85 +
   10.86 +    private:
   10.87 +        Ottoman(const Ottoman&);            // forbid copying/assignment
   10.88 +        Ottoman& operator=(const Ottoman&);
   10.89 +        
   10.90 +        void _open();
   10.91 +        void _append (File *dstFile);
   10.92 +        File* _writeTo (const char *dstFileName, bool overwriteAllowed);
   10.93 +        
   10.94 +        bool _writeable;
   10.95 +        File *_file;
   10.96 +        char *_filename;
   10.97 +        VersionDictionary *_lastVersion;
   10.98 +        OverlayDictionary *_current;
   10.99 +    };
  10.100 +    
  10.101 +}
  10.102 +#endif // _MOOSEYARD_OTTOMAN_
    11.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.2 +++ b/include/VersionDictionary.h	Sun Sep 20 15:14:12 2009 -0700
    11.3 @@ -0,0 +1,127 @@
    11.4 +/*
    11.5 + *  VersionDictionary.h
    11.6 + *  Ottoman
    11.7 + *
    11.8 + *  Created by Jens Alfke on 8/21/09.
    11.9 + *  Copyright 2009 Jens Alfke. All rights reserved.
   11.10 + *  BSD-Licensed: See the file "LICENSE.txt" for details.
   11.11 + */
   11.12 +
   11.13 +#ifndef _MOOSEYARD_VERSIONDICTIONARY_
   11.14 +#define _MOOSEYARD_VERSIONDICTIONARY_
   11.15 +#include "Dictionary.h"
   11.16 +
   11.17 +namespace Mooseyard {
   11.18 +    
   11.19 +    class File;
   11.20 +    class Index;
   11.21 +    
   11.22 +    /** A persistent Dictionary embedded in a memory-mapped file, 
   11.23 +        representing one complete version of an Ottoman dictionary. */
   11.24 +    class VersionDictionary :public Dictionary {
   11.25 +    public:
   11.26 +        
   11.27 +        /** The generation number. The first version in a new file is zero. */
   11.28 +        int generation() const;
   11.29 +        
   11.30 +        /** The absolute time at which this version was written to the file. */
   11.31 +        time_t timestamp() const;
   11.32 +        
   11.33 +        /** The previous version, before this one was saved. */
   11.34 +        const VersionDictionary* previousVersion() const;
   11.35 +        
   11.36 +        /** Is this the latest version in the file? */
   11.37 +        bool isLatestVersion() const;
   11.38 +        
   11.39 +        virtual int count() const;
   11.40 +        virtual Blob get (Key) const;
   11.41 +        class Iterator;
   11.42 +        virtual Dictionary::Iterator* iterate() const;
   11.43 +        
   11.44 +        class ChangeIterator;
   11.45 +        /** Iterates over the changes between this dictionary and the previous version. */
   11.46 +        virtual ChangeIterator* iterateChanges() const;
   11.47 +
   11.48 +        
   11.49 +        // Stuff you probably won't need to use:
   11.50 +        
   11.51 +        File* file() const                                          {return _file;}
   11.52 +        explicit VersionDictionary (File*);
   11.53 +        VersionDictionary (File *file, FilePosition trailerPosition);
   11.54 +        
   11.55 +    private:
   11.56 +        /** The type of the trailer chunk that's used as the position of the dictionary. */
   11.57 +        static const uint16_t kChunkType = 3;
   11.58 +        
   11.59 +        struct IndexPositions {
   11.60 +            LittleEndian<FilePosition> position[256];
   11.61 +            LittleEndian<FilePosition>& operator[] (int i)              {return position[i];}
   11.62 +            LittleEndian<FilePosition> const& operator[] (int i) const  {return position[i];}
   11.63 +        };
   11.64 +                
   11.65 +        class Trailer;
   11.66 +        const Trailer* _trailer() const;
   11.67 +        const Index* _index (int i) const;
   11.68 +        void _read (FilePosition trailerPosition =0);
   11.69 +        static FilePosition _append (const VersionDictionary *baseDict, 
   11.70 +                                     const Dictionary *addDict, 
   11.71 +                                     File *dstFile, 
   11.72 +                                     bool replace);
   11.73 +        
   11.74 +        File *_file;
   11.75 +        FilePosition _trailerPosition, _previousTrailerPosition;
   11.76 +        IndexPositions _indexPositions;
   11.77 +        int _count;
   11.78 +        mutable const VersionDictionary *_previousVersion;
   11.79 +        
   11.80 +#if VERSIONDICTIONARY_TESTING
   11.81 +    public:
   11.82 +#endif
   11.83 +        static VersionDictionary* create (File *file, const Dictionary *srcDict);
   11.84 +        VersionDictionary* byAppending (const Dictionary* d) const     {return _appendAndOpen(d,_file,false);}
   11.85 +        VersionDictionary* byReplacing (const Dictionary* d) const     {return _appendAndOpen(d,_file,true);}
   11.86 +        VersionDictionary* _appendAndOpen (const Dictionary *addDict, File *dstFile, bool replace) const;
   11.87 +        
   11.88 +        friend class Ottoman;
   11.89 +        UNCOPYABLE(VersionDictionary);
   11.90 +    };
   11.91 +    
   11.92 +    
   11.93 +    class VersionDictionary::Iterator :public Dictionary::Iterator {
   11.94 +    public:
   11.95 +        explicit Iterator (const VersionDictionary*);
   11.96 +        virtual ~Iterator();
   11.97 +        virtual Key key() const;
   11.98 +        virtual Blob value() const;
   11.99 +        virtual bool next();
  11.100 +        virtual bool hasValue() const;
  11.101 +    private:
  11.102 +        bool nextIndex();
  11.103 +        
  11.104 +        const VersionDictionary *_file;
  11.105 +        int _bucket;
  11.106 +        Dictionary::Iterator *_iter;
  11.107 +        UNCOPYABLE(Iterator);
  11.108 +    };
  11.109 +    
  11.110 +    
  11.111 +    class VersionDictionary::ChangeIterator :public Dictionary::Iterator {
  11.112 +    public:
  11.113 +        explicit ChangeIterator (const VersionDictionary*);
  11.114 +        virtual ~ChangeIterator();
  11.115 +        virtual Key key() const;
  11.116 +        virtual Blob value() const;
  11.117 +        virtual bool next();
  11.118 +        virtual bool hasValue() const;
  11.119 +    private:
  11.120 +        bool nextIndex();
  11.121 +        
  11.122 +        const VersionDictionary *_file;
  11.123 +        int _bucket;
  11.124 +        Dictionary::Iterator *_iter;
  11.125 +        UNCOPYABLE(ChangeIterator);
  11.126 +    };
  11.127 +
  11.128 +}
  11.129 +
  11.130 +#endif //_MOOSEYARD_VERSIONDICTIONARY_
    12.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    12.2 +++ b/src/Base.cpp	Sun Sep 20 15:14:12 2009 -0700
    12.3 @@ -0,0 +1,66 @@
    12.4 +/*
    12.5 + *  Base.cpp
    12.6 + *  Ottoman
    12.7 + *
    12.8 + *  Created by Jens Alfke on 8/18/09.
    12.9 + *  Copyright 2009 Jens Alfke. All rights reserved.
   12.10 + *  BSD-Licensed: See the file "LICENSE.txt" for details.
   12.11 + */
   12.12 +
   12.13 +#include "Base.h"
   12.14 +#include "File.h"
   12.15 +#include <assert.h>
   12.16 +#include <errno.h>
   12.17 +#include <CoreFoundation/CFUUID.h>
   12.18 +
   12.19 +namespace Mooseyard {
   12.20 +    
   12.21 +#pragma mark -
   12.22 +#pragma mark BLOB:
   12.23 +    
   12.24 +    Blob::Blob (const char *str)
   12.25 +        :bytes(str),
   12.26 +         length(str ?strlen(str) :0)
   12.27 +    { }
   12.28 +    
   12.29 +    /*
   12.30 +     struct MutableBlob :public Blob {
   12.31 +         MutableBlob(void *b, size_t len)                        :Blob(b,len) { }
   12.32 +         void* mutableBytes()                                    {return (void*)bytes;}
   12.33 +         void freeBytes()                                        {::free(mutableBytes()); bytes=NULL; length=0;}
   12.34 +     };
   12.35 +     MutableBlob Blob::copyBytes() const {
   12.36 +        MutableBlob copy( malloc(length), length);
   12.37 +        memcpy(copy.mutableBytes(), bytes, length);
   12.38 +        return copy;
   12.39 +    }
   12.40 +    */
   12.41 +        
   12.42 +#pragma mark -
   12.43 +#pragma mark UUID:
   12.44 +  
   12.45 +#if 0
   12.46 +    /** Typical 128-bit universally-unique identifier. */
   12.47 +    class UUID {
   12.48 +    public:
   12.49 +        UUID();
   12.50 +        const void *bytes() const                           {return _bytes;}
   12.51 +        bool operator== (const UUID&) const;
   12.52 +    private:
   12.53 +        uint8_t _bytes[16];
   12.54 +    };
   12.55 +    
   12.56 +    
   12.57 +    UUID::UUID() {
   12.58 +        CFUUIDRef u = CFUUIDCreate(NULL);
   12.59 +        CFUUIDBytes bytes = CFUUIDGetUUIDBytes(u);
   12.60 +        memcpy(&_bytes, &bytes, sizeof(_bytes));
   12.61 +        CFRelease(u);
   12.62 +    }
   12.63 +
   12.64 +    bool UUID::operator== (const UUID& u) const {
   12.65 +        return memcmp(_bytes, u._bytes, sizeof(_bytes)) == 0;
   12.66 +    }
   12.67 +#endif
   12.68 +    
   12.69 +}
    13.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    13.2 +++ b/src/Chunk.cpp	Sun Sep 20 15:14:12 2009 -0700
    13.3 @@ -0,0 +1,135 @@
    13.4 +/*
    13.5 + *  Chunk.cpp
    13.6 + *  Ottoman
    13.7 + *
    13.8 + *  Created by Jens Alfke on 8/27/09.
    13.9 + *  Copyright 2009 Jens Alfke. All rights reserved.
   13.10 + *  BSD-Licensed: See the file "LICENSE.txt" for details.
   13.11 + */
   13.12 +
   13.13 +#include "Chunk.h"
   13.14 +#include <assert.h>
   13.15 +#include <errno.h>
   13.16 +#include <stddef.h>
   13.17 +
   13.18 +namespace Mooseyard {
   13.19 +    
   13.20 +    uint16_t Chunk::type() const {
   13.21 +        return _keyLength == 0xFFFF ?(uint16_t)_type :KeyValueChunk::kChunkType;
   13.22 +    }
   13.23 +    
   13.24 +    const KeyValueChunk* Chunk::asKeyValue() const {
   13.25 +        return _keyLength == 0xFFFF ?NULL :(const KeyValueChunk*)this;
   13.26 +    }
   13.27 +
   13.28 +    size_t Chunk::writeMultiple (File *file, uint16_t type,
   13.29 +                                 Blob blobs[], int count)  throw(File::Error) {
   13.30 +        assert(type != KeyValueChunk::kChunkType);
   13.31 +        Blob nuBlobs[count+1];
   13.32 +        uint32_t size = sizeof(Chunk);
   13.33 +        for (int i=0; i<count; i++) {
   13.34 +            nuBlobs[i+1] = blobs[i];
   13.35 +            size += blobs[i].length;
   13.36 +        }
   13.37 +        Chunk chunk(size, type);
   13.38 +        nuBlobs[0] = Blob(&chunk, sizeof(chunk));
   13.39 +        return file->writeMultiple(nuBlobs, count+1);
   13.40 +    }
   13.41 +    
   13.42 +    size_t Chunk::writePadding (File *file) {
   13.43 +        int padding = file->position() & 0x03;
   13.44 +        if (padding == 0)
   13.45 +            return 0;
   13.46 +        else {
   13.47 +            padding = 4 - padding;
   13.48 +            uint32_t zero = 0;
   13.49 +            Blob pad(&zero, padding);
   13.50 +            return writeMultiple(file, kChunkTypePadding, &pad, 1);
   13.51 +        }
   13.52 +    }
   13.53 +    
   13.54 +    
   13.55 +    
   13.56 +    void KeyValueChunk::validate() const  throw(File::Error) {
   13.57 +        if (_keyLength >= 0xFFFFU)
   13.58 +            throw File::Error(ERANGE, "Invalid key length (0xFFFF)");
   13.59 +        if (valuePtr() > end())
   13.60 +            throw File::Error(ERANGE, "Bad KeyValueChunk (key-length too large to fit)");
   13.61 +    }
   13.62 +    
   13.63 +    Blob KeyValueChunk::key() const {
   13.64 +        return Blob(&_keyLength +1, _keyLength);  // KeyValueChunk doesn't have _type
   13.65 +    }
   13.66 +    
   13.67 +    const char* KeyValueChunk::valuePtr() const {
   13.68 +        size_t vptr = (size_t) key().end();
   13.69 +        vptr = (vptr+3) & ~3;     // 4-byte align
   13.70 +        return (const char*)vptr;
   13.71 +    }
   13.72 +    
   13.73 +    Blob KeyValueChunk::value() const {
   13.74 +        const char *vp = valuePtr();
   13.75 +        return Blob(vp, (const char*)end()-vp);
   13.76 +    }
   13.77 +    
   13.78 +    bool KeyValueChunk::hasKey (Blob key) const {
   13.79 +        return key.length==_keyLength 
   13.80 +            && memcmp(key.bytes, &_keyLength +1, key.length)==0;
   13.81 +    }
   13.82 +    
   13.83 +    size_t KeyValueChunk::write (File* file, FilePosition pos, Blob key, Blob value)  throw(File::Error) {
   13.84 +        if (key.length >= 0xFFFFU)
   13.85 +            throw File::Error(ERANGE, "Key too long (>=64k)");
   13.86 +        uint16_t keyLength = key.length;
   13.87 +        uint32_t size = sizeof(uint32_t) + sizeof(uint16_t) + keyLength;
   13.88 +        size_t pad = (pos + size) & 0x3;
   13.89 +        if (pad)
   13.90 +            pad = 4-pad;
   13.91 +        uint32_t zeros = 0;
   13.92 +        size += pad + value.length;
   13.93 +        
   13.94 +        Chunk chunk(size, KeyValueChunk::kChunkType);
   13.95 +        chunk._keyLength = keyLength;
   13.96 +        Blob b[4] = {
   13.97 +            Blob(&chunk, sizeof(chunk._size)+sizeof(chunk._keyLength)),
   13.98 +            key,
   13.99 +            Blob(&zeros, pad),
  13.100 +            value};
  13.101 +        return file->writeMultiple(b,4);
  13.102 +    }
  13.103 +
  13.104 +    
  13.105 +    
  13.106 +    ChunkIterator::ChunkIterator (File* file, FilePosition start)
  13.107 +        :_file(file),
  13.108 +         _pos(start),
  13.109 +         _length(_file->length()),
  13.110 +         _chunk(NULL)
  13.111 +    { 
  13.112 +        _loadChunk();
  13.113 +    }
  13.114 +    
  13.115 +    void ChunkIterator::_loadChunk() {
  13.116 +        if (_pos < _length) {
  13.117 +            _chunk = (const Chunk*) _file->mappedPosition(_pos);
  13.118 +            if (_chunk->size() < sizeof(Chunk) || _pos+_chunk->size() > _length)
  13.119 +                _chunk = NULL;
  13.120 +        } else {
  13.121 +            _chunk = NULL;
  13.122 +        }
  13.123 +
  13.124 +    }
  13.125 +    
  13.126 +    bool ChunkIterator::atEOF() const {
  13.127 +        return _pos == _length;
  13.128 +    }
  13.129 +    
  13.130 +    bool ChunkIterator::next() {
  13.131 +        if (_chunk) {
  13.132 +            _pos += _chunk->size();
  13.133 +            _loadChunk();
  13.134 +        }
  13.135 +        return _chunk != NULL;
  13.136 +    }
  13.137 +    
  13.138 +}
    14.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    14.2 +++ b/src/Dictionary.cpp	Sun Sep 20 15:14:12 2009 -0700
    14.3 @@ -0,0 +1,376 @@
    14.4 +/*
    14.5 + *  Dictionary.cpp
    14.6 + *  Ottoman
    14.7 + *
    14.8 + *  Created by Jens Alfke on 8/23/09.
    14.9 + *  Copyright 2009 Jens Alfke. All rights reserved.
   14.10 + *  BSD-Licensed: See the file "LICENSE.txt" for details.
   14.11 + */
   14.12 +
   14.13 +#include "Dictionary.h"
   14.14 +#include "Hash.h"
   14.15 +#include <assert.h>
   14.16 +#include <algorithm>
   14.17 +#include <math.h>
   14.18 +
   14.19 +namespace Mooseyard {
   14.20 +    
   14.21 +    Dictionary::Dictionary() {
   14.22 +    }
   14.23 +    
   14.24 +    Dictionary::~Dictionary() {
   14.25 +    }
   14.26 +    
   14.27 +    Dictionary::Iterator::~Iterator() {
   14.28 +    }
   14.29 +    
   14.30 +    
   14.31 +    const int Dictionary::kMinSize = 8;
   14.32 +    const float Dictionary::kMinLoadFactor = 0.25;
   14.33 +    const float Dictionary::kMaxLoadFactor = 0.75;
   14.34 +        
   14.35 +    // Choose the smallest power of two that's large enough to hold the given capacity
   14.36 +    // of entries with no greater than the given load (fraction full).
   14.37 +    int Dictionary::choosePowerOfTwoSize (int capacity, float load) {
   14.38 +        int idealSize = capacity / load;
   14.39 +        int size;
   14.40 +        for (size=kMinSize; size<idealSize; size *= 2)
   14.41 +            ;
   14.42 +        return size;
   14.43 +    }
   14.44 +    
   14.45 +    int Dictionary::chooseAnyOldSize (int capacity, float load) {
   14.46 +        return std::max((int)::ceil(capacity / load), 2);
   14.47 +    }
   14.48 +    
   14.49 +    
   14.50 +    //-----------------------------------------------------------------------------
   14.51 +    // MurmurHashNeutral2, by Austin Appleby
   14.52 +    // http://murmurhash.googlepages.com/MurmurHashNeutral2.cpp
   14.53 +    
   14.54 +    // Same as MurmurHash2, but endian- and alignment-neutral.
   14.55 +    // Half the speed though, alas.
   14.56 +    
   14.57 +    static uint32_t MurmurHashNeutral2 ( const void * key, int32_t len, uint32_t seed )
   14.58 +    {
   14.59 +        static const uint32_t m = 0x5bd1e995;
   14.60 +        static const int32_t r = 24;
   14.61 +        
   14.62 +        uint32_t h = seed ^ len;
   14.63 +        
   14.64 +        const unsigned char * data = (const unsigned char *)key;
   14.65 +        
   14.66 +        while(len >= 4)
   14.67 +        {
   14.68 +            uint32_t k;
   14.69 +            
   14.70 +            k  = data[0];
   14.71 +            k |= data[1] << 8;
   14.72 +            k |= data[2] << 16;
   14.73 +            k |= data[3] << 24;
   14.74 +            
   14.75 +            k *= m; 
   14.76 +            k ^= k >> r; 
   14.77 +            k *= m;
   14.78 +            
   14.79 +            h *= m;
   14.80 +            h ^= k;
   14.81 +            
   14.82 +            data += 4;
   14.83 +            len -= 4;
   14.84 +        }
   14.85 +        
   14.86 +        switch(len)
   14.87 +        {
   14.88 +            case 3: h ^= data[2] << 16;
   14.89 +            case 2: h ^= data[1] << 8;
   14.90 +            case 1: h ^= data[0];
   14.91 +                h *= m;
   14.92 +        };
   14.93 +        
   14.94 +        h ^= h >> 13;
   14.95 +        h *= m;
   14.96 +        h ^= h >> 15;
   14.97 +        
   14.98 +        return h;
   14.99 +    } 
  14.100 +    
  14.101 +    HashCode Key::computeHash (Blob key) {
  14.102 +        return MurmurHashNeutral2(key.bytes, key.length, 0);
  14.103 +    }
  14.104 +    
  14.105 +    
  14.106 +    
  14.107 +    void MutableDictionary::add (Dictionary* dict) {
  14.108 +        Iterator *it = dict->iterate();
  14.109 +        for (; *it; it->next())
  14.110 +            put(it->key(), it->value());
  14.111 +        delete it;
  14.112 +    }
  14.113 +    
  14.114 +    
  14.115 +        
  14.116 +    const Dictionary& Dictionary::kEmpty = * new HashDictionary();
  14.117 +    
  14.118 +
  14.119 +#pragma mark -
  14.120 +#pragma mark KEY AND VALUE
  14.121 +    
  14.122 +    class KeyAndValue {
  14.123 +    public:
  14.124 +        static KeyAndValue* create (Blob key, Blob value) {
  14.125 +            size_t size = 2*sizeof(uint32_t) + key.length;
  14.126 +            size = (size + 0x3) & ~0x3; // 32-bit align start of value
  14.127 +            if (value!=kDeletedValue)
  14.128 +                size += value.length;
  14.129 +            KeyAndValue *kv = (KeyAndValue*) ::operator new(size);
  14.130 +            kv->_keyLength = key.length;
  14.131 +            memcpy(&kv->_keyLength + 1, key.bytes, key.length);
  14.132 +            uint32_t *valueLengthPtr = const_cast<uint32_t*>(kv->valueLengthPtr());
  14.133 +            *valueLengthPtr = value.length;
  14.134 +            if (value!=kDeletedValue)
  14.135 +                memcpy(valueLengthPtr + 1, value.bytes, value.length);
  14.136 +            return kv;
  14.137 +        }
  14.138 +        Blob key() const {
  14.139 +            return Blob(&_keyLength+1, _keyLength);
  14.140 +        }
  14.141 +        
  14.142 +        Blob value() const{
  14.143 +            const uint32_t *v = this->valueLengthPtr();
  14.144 +            if (*v != kDeletedValue.length)
  14.145 +                return Blob(v+1, *v);
  14.146 +            else
  14.147 +                return kDeletedValue;
  14.148 +        }
  14.149 +
  14.150 +        /** Magic value OverlayDictionary stores in me to represent a deleted value. */
  14.151 +        static const Blob kDeletedValue;
  14.152 +    private:
  14.153 +        const uint32_t* valueLengthPtr() const {
  14.154 +            size_t ptr = (size_t)(&_keyLength + 1) + _keyLength;
  14.155 +            ptr = (ptr + 0x3) & ~0x3;       // 32-bit align
  14.156 +            return (uint32_t*)ptr;
  14.157 +        }
  14.158 +        
  14.159 +        uint32_t _keyLength;
  14.160 +        //...there follows the key, valueLength and value...
  14.161 +    };
  14.162 +
  14.163 +    const Blob KeyAndValue::kDeletedValue(NULL, (size_t)-1);
  14.164 +
  14.165 +    
  14.166 +
  14.167 +    
  14.168 +#pragma mark -
  14.169 +#pragma mark HASH DICTIONARY:
  14.170 +    
  14.171 +    HashDictionary::~HashDictionary() {
  14.172 +        removeAll();
  14.173 +    }
  14.174 +    
  14.175 +    Blob HashDictionary::_convertValue (void *value) {
  14.176 +        return value ?((KeyAndValue*)value)->value() :Blob();
  14.177 +    }
  14.178 +    
  14.179 +    bool HashDictionary::contains (Key key) const {
  14.180 +        Blob value = get(key);
  14.181 +        return value.bytes || value.length;
  14.182 +    }
  14.183 +
  14.184 +    
  14.185 +    void HashDictionary::put (Key key, Blob value) {
  14.186 +        // Allocate a block to store both the Blob struct and the data
  14.187 +        KeyAndValue *kv = KeyAndValue::create(key,value);
  14.188 +        _hash.put(Key(kv->key(),key.hash), kv);
  14.189 +    }
  14.190 +    
  14.191 +    bool HashDictionary::remove (Key key) {
  14.192 +        KeyAndValue *kv = (KeyAndValue*) _hash.get(key);
  14.193 +        if (kv) {
  14.194 +            free(kv);
  14.195 +            return true;
  14.196 +        } else
  14.197 +            return false;
  14.198 +    }
  14.199 +    
  14.200 +    void HashDictionary::removeAll() {
  14.201 +        for (Hash::Iterator it(&_hash); it; ++it)
  14.202 +            free(it.value());
  14.203 +        _hash.removeAll();
  14.204 +    }
  14.205 +        
  14.206 +    
  14.207 +    
  14.208 +    
  14.209 +#pragma mark -
  14.210 +#pragma mark OVERLAY DICTIONARY:
  14.211 +    
  14.212 +    
  14.213 +    OverlayDictionary::OverlayDictionary (const Dictionary *base)
  14.214 +    :_base(base),
  14.215 +     _overlay(new HashDictionary()),
  14.216 +     _count(base->count()),
  14.217 +     _allRemoved(false)
  14.218 +    { }
  14.219 +    
  14.220 +    OverlayDictionary::~OverlayDictionary() {
  14.221 +        delete _overlay;
  14.222 +    }
  14.223 +    
  14.224 +    int OverlayDictionary::count() const {
  14.225 +        return _count;
  14.226 +    }
  14.227 +    
  14.228 +    bool OverlayDictionary::isChanged() const {
  14.229 +        return _overlay->count() > 0;
  14.230 +    }
  14.231 +    
  14.232 +    Blob OverlayDictionary::get (Key key) const {
  14.233 +        Blob result = _overlay->get(key);
  14.234 +        if (!result) {
  14.235 +            if (result==KeyAndValue::kDeletedValue)
  14.236 +                result = Blob();                // It's been marked as deleted
  14.237 +            else if (!_allRemoved)
  14.238 +                result = _base->get(key);
  14.239 +        }
  14.240 +        return result;
  14.241 +    }
  14.242 +    
  14.243 +    bool OverlayDictionary::contains (Key key) const {
  14.244 +        Blob result = _overlay->get(key);
  14.245 +        if (result)
  14.246 +            return true;
  14.247 +        else if (result==KeyAndValue::kDeletedValue)
  14.248 +            return false;
  14.249 +        else if (!_allRemoved)
  14.250 +            return _base->get(key);
  14.251 +        else 
  14.252 +            return false;
  14.253 +    }
  14.254 +    
  14.255 +    void OverlayDictionary::put (Key key, Blob value) {
  14.256 +        assert(value.bytes || value.length==0);    // make sure it doesn't look like magic kDeletedValue
  14.257 +        _overlay->put(key,value);
  14.258 +        if (_allRemoved || !_base->contains(key))
  14.259 +            _count++;
  14.260 +    }
  14.261 +    
  14.262 +    bool OverlayDictionary::remove (Key key) {
  14.263 +        if (!_allRemoved && _base->contains(key)) {
  14.264 +            _overlay->put(key,KeyAndValue::kDeletedValue);
  14.265 +            _count--;
  14.266 +            return true;
  14.267 +        } else if (_overlay->remove(key)) {
  14.268 +            _count--;
  14.269 +            return true;
  14.270 +        } else
  14.271 +            return false;
  14.272 +    }
  14.273 +    
  14.274 +    void OverlayDictionary::removeAll() {
  14.275 +        _allRemoved = true;
  14.276 +        _count = 0;
  14.277 +    }
  14.278 +    
  14.279 +    
  14.280 +    void OverlayDictionary::revert() {
  14.281 +        _overlay->removeAll();
  14.282 +        _count = _base->count();
  14.283 +        _allRemoved = false;
  14.284 +    }
  14.285 +    
  14.286 +    void OverlayDictionary::revertTo (const Dictionary* newBase) {
  14.287 +        _base = newBase;
  14.288 +        revert();
  14.289 +    }
  14.290 +    
  14.291 +    void OverlayDictionary::rebase (const Dictionary* newBase) {
  14.292 +        _base = newBase;
  14.293 +    }
  14.294 +
  14.295 +    OverlayDictionary::Iterator::Iterator (const OverlayDictionary &dict)
  14.296 +        :_iter(dict.base()->iterate()),
  14.297 +         _overlay(dict._overlay)
  14.298 +    {
  14.299 +        if (skipCurrentState())
  14.300 +            next();
  14.301 +    }
  14.302 +    
  14.303 +    Key OverlayDictionary::Iterator::key() const        {return _iter ?_iter->key() :Key();}
  14.304 +    Blob OverlayDictionary::Iterator::value() const     {return _iter ?_iter->value() :Blob();}
  14.305 +    
  14.306 +    bool OverlayDictionary::Iterator::next() {
  14.307 +        if (_iter) {
  14.308 +            do {
  14.309 +                _iter->next();
  14.310 +            } while (skipCurrentState());
  14.311 +        }
  14.312 +        return _iter && *_iter;
  14.313 +    }
  14.314 +    
  14.315 +    bool OverlayDictionary::Iterator::skipCurrentState() {
  14.316 +        if (_iter) {
  14.317 +            if (*_iter) {
  14.318 +                if (_overlay) {
  14.319 +                    if (_overlay->contains(_iter->key()))
  14.320 +                        return true;        // Skip overridden value in base dict
  14.321 +                } else {
  14.322 +                    if (_iter->value() == KeyAndValue::kDeletedValue)
  14.323 +                        return true;        // Skip marked-deleted value in overlay dict
  14.324 +                }
  14.325 +            } else {
  14.326 +                delete _iter;
  14.327 +                if (_overlay) {
  14.328 +                    // end of base iterator; switch to overlay
  14.329 +                    _iter = _overlay->iterate();
  14.330 +                    _overlay = NULL;
  14.331 +                    return skipCurrentState();
  14.332 +                } else {
  14.333 +                    _iter = NULL;
  14.334 +                }
  14.335 +            }
  14.336 +        }
  14.337 +        return false;
  14.338 +    }
  14.339 +    
  14.340 +    
  14.341 +#pragma mark -
  14.342 +#pragma mark CHANGE ITERATOR:
  14.343 +    
  14.344 +    
  14.345 +    Dictionary::ChangeIterator::ChangeIterator (const Dictionary *dict1, const Dictionary *dict2) {
  14.346 +        _init(dict1, dict2);
  14.347 +    }
  14.348 +    
  14.349 +    Dictionary::ChangeIterator::ChangeIterator (const OverlayDictionary *overlay) {
  14.350 +        _init(overlay->_overlay, overlay->base());
  14.351 +    }
  14.352 +    
  14.353 +    void Dictionary::ChangeIterator::_init(const Dictionary *dict1, const Dictionary *dict2) {
  14.354 +        _dict2 = dict2;
  14.355 +        _iter = dict1->iterate();
  14.356 +        if (*_iter)
  14.357 +            _skipMatching();
  14.358 +    }
  14.359 +    
  14.360 +    Dictionary::ChangeIterator::~ChangeIterator() {
  14.361 +        delete _iter;
  14.362 +    }
  14.363 +    
  14.364 +    bool Dictionary::ChangeIterator::next() {
  14.365 +        return _iter->next() && this->_skipMatching();
  14.366 +    }
  14.367 +    
  14.368 +    bool Dictionary::ChangeIterator::_skipMatching() {
  14.369 +        do{
  14.370 +            _otherValue = _dict2->get(_iter->key());
  14.371 +            if (!_otherValue.equals(_iter->value()))
  14.372 +                return true;
  14.373 +        }while (_iter->next());
  14.374 +        return false;
  14.375 +    }
  14.376 +    
  14.377 +    
  14.378 +
  14.379 +}
    15.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    15.2 +++ b/src/File.cpp	Sun Sep 20 15:14:12 2009 -0700
    15.3 @@ -0,0 +1,214 @@
    15.4 +/*
    15.5 + *  File.cpp
    15.6 + *  Ottoman
    15.7 + *
    15.8 + *  Created by Jens Alfke on 8/20/09.
    15.9 + *  Copyright 2009 Jens Alfke. All rights reserved.
   15.10 + *  BSD-Licensed: See the file "LICENSE.txt" for details.
   15.11 + */
   15.12 +
   15.13 +#include "File.h"
   15.14 +#include "MemoryMap.h"
   15.15 +
   15.16 +#include <assert.h>
   15.17 +#include <errno.h>
   15.18 +#include <fcntl.h>
   15.19 +#include <stdio.h>
   15.20 +#include <sys/stat.h>
   15.21 +#include <sys/uio.h>
   15.22 +#include <unistd.h>
   15.23 +
   15.24 +namespace Mooseyard {
   15.25 +    
   15.26 +    File::File (const char *filename, bool writeable, bool create)  throw(Error)
   15.27 +        :_fd(_check( ::open(filename, writeable ?(create ?O_RDWR|O_CREAT :O_RDWR) :O_RDONLY, 0644) )),
   15.28 +         _memoryMap(NULL),
   15.29 +         _locked(0)
   15.30 +    { }
   15.31 +
   15.32 +    File::File (const char *filename, int oflag)  throw(Error)
   15.33 +        :_fd(_check( ::open(filename, oflag, 0644) )),
   15.34 +         _memoryMap(NULL),
   15.35 +         _locked( (oflag | O_EXLOCK) ?1 :0)
   15.36 +    { }
   15.37 +
   15.38 +    File::~File() {
   15.39 +        delete _memoryMap;
   15.40 +        _unlock();
   15.41 +        ::close(_fd);
   15.42 +    }
   15.43 +    
   15.44 +    
   15.45 +    off_t File::length() const  throw(Error) {
   15.46 +        struct stat s;
   15.47 +        _check( ::fstat(_fd,&s) );
   15.48 +        return s.st_size;
   15.49 +    }
   15.50 +     
   15.51 +    void File::setLength (off_t length)  throw(Error) {
   15.52 +        _check( ftruncate(_fd,length) );
   15.53 +    }
   15.54 +
   15.55 +    off_t File::position() const  throw(Error) {
   15.56 +        return _check( lseek(_fd,0,SEEK_CUR) );
   15.57 +    }
   15.58 +    
   15.59 +    void File::setPosition (off_t pos)  throw(Error) {
   15.60 +        _check( lseek(_fd,pos,SEEK_SET) );
   15.61 +    }
   15.62 +    
   15.63 +    off_t File::setPositionToEnd (off_t bytesBefore)  throw(Error) {
   15.64 +        return _check( lseek(_fd,-bytesBefore,SEEK_END) );
   15.65 +    }
   15.66 +    
   15.67 +    void File::read (void *dst, size_t size)  throw(Error) {
   15.68 +        _checkRead( ::read(_fd, dst, size), size );
   15.69 +    }
   15.70 +    
   15.71 +    size_t File::write (const void *src, size_t size)  throw(Error) {
   15.72 +        return _check( ::write(_fd, src, size) );
   15.73 +    }
   15.74 +    
   15.75 +    
   15.76 +    void File::readFrom (off_t where, void *dst, size_t size)  throw(Error) {
   15.77 +        _checkRead( ::pread(_fd, dst, size, where), size );
   15.78 +    }
   15.79 +    
   15.80 +    void File::writeTo (off_t where, const void *src, size_t size)  throw(Error) {
   15.81 +        _check( ::pwrite(_fd, src, size, where) );
   15.82 +    }
   15.83 +    
   15.84 +    size_t File::writeMultiple (Blob blobs[], int count) throw(Error) {
   15.85 +        struct iovec vec[count];
   15.86 +        for (int i=0; i<count; i++) {
   15.87 +            vec[i].iov_base = (void*) blobs[i].bytes;
   15.88 +            vec[i].iov_len = blobs[i].length;
   15.89 +        }
   15.90 +        return _check( ::writev(_fd, vec, count) );
   15.91 +    }
   15.92 +    
   15.93 +    size_t File::writePadding (int alignment) {
   15.94 +        off_t pad = alignment - (position() & (alignment-1));
   15.95 +        if (pad == alignment)
   15.96 +            return 0;
   15.97 +        uint8_t zero[pad];
   15.98 +        memset(zero, 0, pad);
   15.99 +        return write(zero, pad);
  15.100 +    }
  15.101 +    
  15.102 +    
  15.103 +    void File::flush()  throw(Error) {
  15.104 +        _check( ::fsync(_fd) );
  15.105 +    }
  15.106 +    
  15.107 +    void File::flushDisk()  throw(Error) {
  15.108 +        _check( ::fcntl(_fd,F_FULLFSYNC) );
  15.109 +    }
  15.110 +    
  15.111 +    bool File::hasPath (const char *path) const  throw(Error) {
  15.112 +        struct stat myStat, pathStat;
  15.113 +        _check( ::fstat(_fd, &myStat) );
  15.114 +        if ( ::stat(path, &pathStat) != 0 ) {
  15.115 +            if (errno == ENOENT)
  15.116 +                return false;
  15.117 +            _check(errno);
  15.118 +        }
  15.119 +        // Compare my inode number with that of the file at path:
  15.120 +        return myStat.st_ino == pathStat.st_ino;
  15.121 +    }
  15.122 +    
  15.123 +    
  15.124 +    void File::unlink (const char *filename)  throw(Error) {
  15.125 +        _check( ::unlink(filename) );
  15.126 +    }
  15.127 +    
  15.128 +    void File::rename (const char *srcFilename, const char *dstFilename)  throw(Error) {
  15.129 +        _check( ::rename(srcFilename, dstFilename) );
  15.130 +    }
  15.131 +
  15.132 +    
  15.133 +#pragma mark -
  15.134 +#pragma mark UTILITIES:
  15.135 +    
  15.136 +    int File::_check (int result)  throw(Error) {
  15.137 +        if (result >= 0)
  15.138 +            return result;
  15.139 +        //printf("*** File::_check: Error %i: %s\n", errno, strerror(errno));
  15.140 +        throw Error(errno, strerror(errno));
  15.141 +    }
  15.142 +    
  15.143 +    void File::_checkRead (ssize_t result, size_t expectedSize)  throw(Error) {
  15.144 +        if ((size_t)result < expectedSize) {
  15.145 +            if (result < 0)
  15.146 +                throw Error(errno, strerror(errno));
  15.147 +            else
  15.148 +                throw Error(kEOF, "unexpected EOF");
  15.149 +        }
  15.150 +    }
  15.151 +    
  15.152 +    File::Error::Error(const char *m)
  15.153 +        :code(ERANGE), 
  15.154 +         message(m) 
  15.155 +    { }
  15.156 +    
  15.157 +    
  15.158 +#pragma mark -
  15.159 +#pragma mark LOCKS:
  15.160 +    
  15.161 +    bool File::_lock (bool block) {
  15.162 +        if (_locked) {
  15.163 +            _locked++;
  15.164 +        } else {
  15.165 +            int mode = LOCK_EX;
  15.166 +            if (block)
  15.167 +                mode |= LOCK_NB;
  15.168 +            if (::flock(_fd, mode) == 0)
  15.169 +                _locked = 1;
  15.170 +            else if (errno != EWOULDBLOCK)          // may be returned in LOCK_UN mode
  15.171 +                _check(-1);
  15.172 +        }
  15.173 +        return _locked > 0;
  15.174 +    }
  15.175 +    
  15.176 +    void File::_unlock() {
  15.177 +        if (_locked > 0) {
  15.178 +            _locked--;
  15.179 +            ::flock(_fd, LOCK_UN);
  15.180 +        }
  15.181 +    }
  15.182 +    
  15.183 +    File::Lock::Lock (File *file, bool block)  throw(Error)
  15.184 +        :_file(file),
  15.185 +         _locked( _file->_lock(block) )
  15.186 +    { }
  15.187 +    
  15.188 +    File::Lock::~Lock() {
  15.189 +        if (_locked)
  15.190 +            _file->_unlock();
  15.191 +    }
  15.192 +    
  15.193 +    bool File::Lock::retry() {
  15.194 +        if (!_locked)
  15.195 +            _locked = _file->_lock(false);
  15.196 +        return _locked;
  15.197 +    }
  15.198 +    
  15.199 +    
  15.200 +#pragma mark -
  15.201 +#pragma mark MEMORY MAP:
  15.202 +    
  15.203 +    MemoryMap* File::map() {
  15.204 +        if (!_memoryMap)
  15.205 +            _memoryMap = new MemoryMap(this);
  15.206 +        return _memoryMap;
  15.207 +    }
  15.208 +    
  15.209 +    void File::mapRegion (off_t position, size_t length) {
  15.210 +        return map()->mapRegion(position,length);
  15.211 +    }
  15.212 +    
  15.213 +    const void* File::mappedPosition (off_t position) const {
  15.214 +        return map()->mappedPosition(position);
  15.215 +    }
  15.216 +
  15.217 +}
    16.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    16.2 +++ b/src/Hash.cpp	Sun Sep 20 15:14:12 2009 -0700
    16.3 @@ -0,0 +1,160 @@
    16.4 +/*
    16.5 + *  Hash.cpp
    16.6 + *  Ottoman
    16.7 + *
    16.8 + *  Created by Jens Alfke on 8/20/09.
    16.9 + *  Copyright 2009 Jens Alfke. All rights reserved.
   16.10 + *  BSD-Licensed: See the file "LICENSE.txt" for details.
   16.11 + */
   16.12 +
   16.13 +#include "Hash.h"
   16.14 +#include "Dictionary.h"
   16.15 +#include <algorithm>
   16.16 +
   16.17 +namespace Mooseyard {
   16.18 +    
   16.19 +    class Hash::IndexEntry {
   16.20 +    public:
   16.21 +        IndexEntry (Key key, Hash::Value value =NULL)
   16.22 +            :_key(key), 
   16.23 +             _value(value) 
   16.24 +        { }
   16.25 +        
   16.26 +        Key key() const                             {return _key;}
   16.27 +        HashCode hash() const                       {return _key.hash;}
   16.28 +        Hash::Value value() const                   {return _key.bytes ?_value :NULL;}
   16.29 +        void setValue (Hash::Value value)           {_value = value;}
   16.30 +        
   16.31 +        bool isAvailable() const                    {return _value == NULL;}    // false if deleted
   16.32 +        bool hasValue() const                       {return _key.bytes != NULL;}// false if deleted
   16.33 +        bool operator==(const IndexEntry &e) const  {return _key.hash==e._key.hash && _key.equals(e._key);}
   16.34 +        bool operator!=(const IndexEntry &e) const  {return ! (*this==e);}
   16.35 +        
   16.36 +        void markDeleted()                          {_key.bytes = NULL; _value = (Hash::Value)-1;}
   16.37 +        void markEmpty()                            {_key.bytes = NULL; _value = NULL;}
   16.38 +        
   16.39 +    private:
   16.40 +        Key _key;                       // bytes will be NULL if empty or deleted
   16.41 +        Hash::Value _value;             // NULL if empty, -1 if deleted
   16.42 +    };
   16.43 +        
   16.44 +            
   16.45 +    Hash::Hash (int capacity)
   16.46 +        :_count(0),
   16.47 +         _tableSize( Dictionary::choosePowerOfTwoSize(capacity) ),
   16.48 +         _table( (IndexEntry*) calloc(_tableSize,sizeof(IndexEntry)) )
   16.49 +    { }
   16.50 +    
   16.51 +    Hash::~Hash() {
   16.52 +        free(_table);
   16.53 +    }
   16.54 +    
   16.55 +    Hash::IndexEntry& Hash::_find (Key key) const {
   16.56 +        Key k = Key(key);
   16.57 +        IndexEntry newEntry(k);
   16.58 +        int index = newEntry.hash() % _tableSize;
   16.59 +        IndexEntry *found = &_table[index];
   16.60 +        int probe = 0;
   16.61 +        while (!found->isAvailable() && (!found->hasValue() || *found != newEntry)) {
   16.62 +            index = (index + ++probe) % _tableSize;
   16.63 +            found = &_table[index];
   16.64 +        }
   16.65 +        return *found;
   16.66 +    }
   16.67 +    
   16.68 +    Hash::IndexEntry& Hash::_findForPut (const Hash::IndexEntry &newEntry) {
   16.69 +        int index = newEntry.hash() % _tableSize;
   16.70 +        IndexEntry *found = &_table[index];
   16.71 +        int probe = 0;
   16.72 +        while (found->hasValue() && *found != newEntry) {
   16.73 +            index = (index + ++probe) % _tableSize;
   16.74 +            found = &_table[index];
   16.75 +        }
   16.76 +        return *found;
   16.77 +    }
   16.78 +    
   16.79 +    Hash::Value Hash::get(Key key) const {
   16.80 +        return _find(key).value();
   16.81 +    }
   16.82 +    
   16.83 +    void Hash::put(Key key, Hash::Value value) {
   16.84 +        IndexEntry newEntry = IndexEntry(Key(key),value);
   16.85 +        IndexEntry &found = _findForPut(newEntry);
   16.86 +        if (found.hasValue())
   16.87 +            found.setValue(value);
   16.88 +        else {
   16.89 +            found = newEntry;
   16.90 +            _count++;
   16.91 +            if (_count/(float)_tableSize > Dictionary::kMaxLoadFactor)
   16.92 +                _resize(2*_tableSize);
   16.93 +        }
   16.94 +    }
   16.95 +    
   16.96 +    bool Hash::remove(Key key) {
   16.97 +        IndexEntry &entry = _find(key);
   16.98 +        if (!entry.value())
   16.99 +            return false;
  16.100 +        entry.markDeleted();
  16.101 +        _count--;
  16.102 +        if (_count/(float)_tableSize < Dictionary::kMinLoadFactor)
  16.103 +            _resize(_tableSize/2);
  16.104 +        return true;
  16.105 +    }
  16.106 +        
  16.107 +    void Hash::removeAll() {
  16.108 +        memset(_table, 0, _tableSize*sizeof(*_table));
  16.109 +        _count = 0;
  16.110 +    }
  16.111 +    
  16.112 +    void Hash::dump() const {
  16.113 +        for (int index=0; index<_tableSize; index++)
  16.114 +            if (_table[index].hasValue()) {
  16.115 +                Blob key = _table[index].key();
  16.116 +                printf("%3i: %*s -> %p\n", 
  16.117 +                       index, (int)key.length,(const char*)key.bytes, _table[index].value());
  16.118 +            }
  16.119 +    }
  16.120 +    
  16.121 +    
  16.122 +    void Hash::_resize(int newSize) {
  16.123 +        newSize = std::max(newSize, Dictionary::kMinSize);
  16.124 +        if (newSize != _tableSize) {
  16.125 +            IndexEntry* oldTable = _table;
  16.126 +            int oldTableSize = _tableSize;
  16.127 +            _tableSize = newSize;
  16.128 +            _table = (IndexEntry*) calloc(_tableSize,sizeof(IndexEntry));
  16.129 +            
  16.130 +            // Add existing entries to new table:
  16.131 +            for (int index=0; index<oldTableSize; index++) {
  16.132 +                IndexEntry &entry = oldTable[index];
  16.133 +                if (entry.value())
  16.134 +                    _findForPut(entry) = entry;
  16.135 +            }
  16.136 +            free(oldTable);
  16.137 +        }
  16.138 +    }
  16.139 +    
  16.140 +    Hash::Iterator::Iterator (const Hash *hash)
  16.141 +        :_entry(hash->_table-1), _end(hash->_table+hash->_tableSize)
  16.142 +    {
  16.143 +        next();
  16.144 +    }
  16.145 +
  16.146 +    Hash::Iterator::Iterator (const Hash &hash)
  16.147 +        :_entry(hash._table-1), _end(hash._table+hash._tableSize)
  16.148 +    {
  16.149 +        next();
  16.150 +    }
  16.151 +
  16.152 +    bool Hash::Iterator::next() {
  16.153 +        while (++_entry < _end) {
  16.154 +            if (_entry->hasValue())
  16.155 +                return true;
  16.156 +        }
  16.157 +        return false;
  16.158 +    }
  16.159 +
  16.160 +    Key Hash::Iterator::key() const                 {return _entry->key();}
  16.161 +    Hash::Value Hash::Iterator::value() const       {return _entry->value();}
  16.162 +    
  16.163 +}
    17.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    17.2 +++ b/src/Index.cpp	Sun Sep 20 15:14:12 2009 -0700
    17.3 @@ -0,0 +1,270 @@
    17.4 +/*
    17.5 + *  Index.cpp
    17.6 + *  Ottoman
    17.7 + *
    17.8 + *  Created by Jens Alfke on 8/27/09.
    17.9 + *  Copyright 2009 Jens Alfke. All rights reserved.
   17.10 + *  BSD-Licensed: See the file "LICENSE.txt" for details.
   17.11 + */
   17.12 +
   17.13 +#include "Index.h"
   17.14 +#include "Chunk.h"
   17.15 +#include "File.h"
   17.16 +#include "Dictionary.h"
   17.17 +#include <assert.h>
   17.18 +#include <errno.h>
   17.19 +#include <algorithm>
   17.20 +#include <math.h>
   17.21 +
   17.22 +namespace Mooseyard {
   17.23 +        
   17.24 +    // Quadratic probing results in fewer bucket tests on lookup, but makes the hash tables
   17.25 +    // significantly larger, which increases the file size. In practice a smaller table size seems
   17.26 +    // to be a bigger win since disk I/O is slow.
   17.27 +    #define PROBE_QUADRATIC 0
   17.28 +
   17.29 +    // Similarly use a higher max load than for memory-resident tables, to save space.
   17.30 +    static const float kMaxLoadFactor = 0.85;
   17.31 +
   17.32 +    static const uint32_t kIndexMagicNumber = 0x54378543;
   17.33 +    
   17.34 +
   17.35 +    /** An index (hash-table) entry as stored in the memory-mapped file. */
   17.36 +    class Index::Entry {
   17.37 +    public:
   17.38 +        explicit Entry()                        :_hash(0), _valuePosition(0) { }
   17.39 +        explicit Entry (HashCode h, FilePosition p)
   17.40 +                                                :_hash(h), _valuePosition(p) { }
   17.41 +        LittleEndian<HashCode> hash() const       {return _hash;}
   17.42 +        bool hasHash (LittleEndian<HashCode> h) const {return _hash == h;}
   17.43 +        
   17.44 +        const KeyValueChunk* value (const File *file) const {
   17.45 +            return (const KeyValueChunk*) file->mappedPosition(_valuePosition);
   17.46 +        }
   17.47 +        FilePosition valuePosition() const      {return _valuePosition;}
   17.48 +        
   17.49 +        
   17.50 +        bool isAvailable() const                {return _valuePosition == 0;}   // false if deleted
   17.51 +        bool hasValue() const                   {return _valuePosition > 1;}    // false if deleted
   17.52 +        
   17.53 +        void markDeleted()                      {_valuePosition = kDeletedPosition;}
   17.54 +        void markEmpty()                        {_valuePosition = 0;}
   17.55 +        
   17.56 +        
   17.57 +        static const FilePosition kDeletedPosition = 1;
   17.58 +                
   17.59 +    private:
   17.60 +        LittleEndian<HashCode> _hash;
   17.61 +        LittleEndian<FilePosition> _valuePosition;
   17.62 +    };
   17.63 +        
   17.64 +    
   17.65 +#pragma mark -
   17.66 +#pragma mark CREATION:
   17.67 +        
   17.68 +    Index::Index (uint32_t itsTableSize)
   17.69 +        :Chunk(sizeof(Index) + itsTableSize*sizeof(Index::Entry), kChunkType),
   17.70 +         _magicNumber(kIndexMagicNumber),
   17.71 +         _count(0),
   17.72 +         _tableSize(itsTableSize)
   17.73 +    { }
   17.74 +    
   17.75 +    Index* Index::create (int capacity) {
   17.76 +#if PROBE_QUADRATIC
   17.77 +        int tableSize = Dictionary::choosePowerOfTwoSize(capacity);
   17.78 +#else
   17.79 +        int tableSize = Dictionary::chooseAnyOldSize(capacity, kMaxLoadFactor);
   17.80 +#endif
   17.81 +        size_t size = sizeof(Index) + tableSize*sizeof(Index::Entry);
   17.82 +        void* storage = ::operator new(size);
   17.83 +        memset(storage,0,size);
   17.84 +        return new(storage) Index(tableSize);
   17.85 +    }
   17.86 +    
   17.87 +    const Index* Index::indexMappedAt (const void* ptr) {
   17.88 +        const Index *index = (const Index*)ptr;
   17.89 +        index->validate();
   17.90 +        return index;
   17.91 +    }
   17.92 +    
   17.93 +    void Index::validate() const  throw(File::Error) {
   17.94 +        if (_magicNumber != kIndexMagicNumber)
   17.95 +            throw File::Error(ERANGE, "Bad Index (wrong magic number)");
   17.96 +        if (_tableSize < 2 || _count > _tableSize)
   17.97 +            throw File::Error(ERANGE, "Bad Index (wrong count or tableSize)");
   17.98 +#if PROBE_QUADRATIC
   17.99 +        if ((_tableSize & (_tableSize-1)) != 0)
  17.100 +            throw File::Error(ERANGE, "Bad Index (size not power of 2)");
  17.101 +#endif
  17.102 +        if (size() != sizeof(Index)+_tableSize*sizeof(Index::Entry))
  17.103 +            throw File::Error(ERANGE, "Bad Index (wrong size)");
  17.104 +    }
  17.105 +    
  17.106 +    void Index::validateEntries (const File *file) const  throw(File::Error) {
  17.107 +        validate();
  17.108 +        uint32_t size = _tableSize;
  17.109 +        uint32_t count = 0;
  17.110 +        const Entry *entry = table(0);
  17.111 +        for (uint32_t i=0; i<size; i++, entry++) {
  17.112 +            if (entry->hasValue()) {
  17.113 +                count++;
  17.114 +                const KeyValueChunk *chunk = entry->value(file);
  17.115 +                if ((void*)chunk->end() > (void*)this)
  17.116 +                    throw File::Error(ERANGE, "Bad Index entry (points beyond index)");
  17.117 +                chunk->validate();
  17.118 +                if (entry->hash() != Key::computeHash(chunk->key()))
  17.119 +                    throw File::Error(ERANGE, "Bad Index entry (wrong hash code)");
  17.120 +            }
  17.121 +        }
  17.122 +        if (count != _count)
  17.123 +            throw File::Error(ERANGE, "Bad Index (inconsistent count)");
  17.124 +    }
  17.125 +    
  17.126 +    
  17.127 +#pragma mark -
  17.128 +#pragma mark LOOKUP:
  17.129 +    
  17.130 +    const Index::Entry* Index::table (int index) const {
  17.131 +        return &((const Index::Entry*)&_table)[index];
  17.132 +    }
  17.133 +    
  17.134 +    Index::Entry* Index::table (int index) {
  17.135 +        return &((Index::Entry*)&_table)[index];
  17.136 +    }
  17.137 +    
  17.138 +        
  17.139 +    const KeyValueChunk* Index::_find (File *file, Key key) const {
  17.140 +        int index = key.hash % _tableSize;
  17.141 +        LittleEndian<HashCode> lhash = key.hash;
  17.142 +#if PROBE_QUADRATIC
  17.143 +        int probe = 0;
  17.144 +#endif
  17.145 +        for(;;) {
  17.146 +            const Index::Entry *found = table(index);
  17.147 +            if (found->isAvailable()) {
  17.148 +                return NULL;
  17.149 +            } else if (found->hasHash(lhash)) {
  17.150 +                const KeyValueChunk *value = found->value(file);
  17.151 +                if (value->hasKey(key)) {
  17.152 +                    return value;
  17.153 +                }
  17.154 +            }
  17.155 +#if PROBE_QUADRATIC
  17.156 +            index = (index + ++probe) % _tableSize;
  17.157 +#else
  17.158 +            index = (index + 1) % _tableSize;
  17.159 +#endif
  17.160 +        }
  17.161 +    }
  17.162 +    
  17.163 +    Blob Index::get (File *file, Key key) const {
  17.164 +        const KeyValueChunk *kv = _find(file, key);
  17.165 +        return kv ?kv->value() :Blob();
  17.166 +    }
  17.167 +    
  17.168 +
  17.169 +    // Common implementation of put and delete. (If deleting, valuePosition will be kDeletedPosition.)
  17.170 +    // While searching, entries past maxPosition should be considered newly-added and not compared,
  17.171 +    // since they're not in the memory-mapped part of the file yet.
  17.172 +    // Returns true if the entry was added/removed.
  17.173 +    bool Index::_put (File *file, Key key, FilePosition valuePosition, FilePosition maxPosition) {
  17.174 +        bool result = (valuePosition <= Entry::kDeletedPosition);
  17.175 +        int index = key.hash % _tableSize;
  17.176 +        Index::Entry entry(key.hash, valuePosition);
  17.177 +#if PROBE_QUADRATIC
  17.178 +        int probe = 0;
  17.179 +#endif
  17.180 +        Index::Entry *found;
  17.181 +        for(;;) {
  17.182 +            found = table(index);
  17.183 +            if (!found->hasValue()) {
  17.184 +                if (entry.hasValue()) {
  17.185 +                    result = true;
  17.186 +                    break;                  // it's a put and we found an empty entry
  17.187 +                } else if (found->isAvailable()) {
  17.188 +                    return false;           // it's a delete but the key wasn't found
  17.189 +                }
  17.190 +            }
  17.191 +            else if (found->hasHash(entry.hash())) {
  17.192 +                if (!file)
  17.193 +                    break;                  // no file given, so assume no duplicate keys
  17.194 +                else if (found->valuePosition() < maxPosition && found->value(file)->hasKey(key))
  17.195 +                    break;                  // found existing (not newly-inserted) entry with the matching key
  17.196 +                /*else
  17.197 +                    printf("** hash collision! hash=%u\n", key.hash);*/
  17.198 +            }
  17.199 +#if PROBE_QUADRATIC
  17.200 +            index = (index + ++probe) % _tableSize;
  17.201 +#else
  17.202 +            index = (index + 1) % _tableSize;
  17.203 +#endif
  17.204 +        }
  17.205 +        // Finally found where to put it:
  17.206 +        *found = entry;
  17.207 +        return result;
  17.208 +    }
  17.209 +        
  17.210 +    bool Index::put (File *file, Key key, FilePosition valuePosition, FilePosition maxPosition) {
  17.211 +        bool added = _put(file, key, valuePosition, maxPosition);
  17.212 +        if (added)
  17.213 +            ++_count;
  17.214 +        return added;
  17.215 +    }
  17.216 +    
  17.217 +    bool Index::remove (File *file, Key key, FilePosition maxPosition) {
  17.218 +        bool removed = _put(file, key, Index::Entry::kDeletedPosition, maxPosition);
  17.219 +        if (removed) {
  17.220 +            assert(_count > 0);
  17.221 +            --_count;
  17.222 +        }
  17.223 +        return removed;
  17.224 +    }
  17.225 +    
  17.226 +    void Index::removeAll () {
  17.227 +        memset(table(0), 0, _tableSize*sizeof(Entry));
  17.228 +        _count = 0;
  17.229 +    }
  17.230 +    
  17.231 +    
  17.232 +    void Index::copyFrom (File *file, const Index *index) {
  17.233 +        const Entry *src = index->table(0);
  17.234 +        if (index->_tableSize == _tableSize) {
  17.235 +            memcpy(table(0), src, _tableSize*sizeof(Entry));
  17.236 +        } else {
  17.237 +            removeAll();
  17.238 +            for (uint32_t i=0; i<index->_tableSize; i++,src++)
  17.239 +                if (src->hasValue())
  17.240 +                    _put(NULL, Key(NULL,0,src->hash()), src->valuePosition(), UINT32_MAX);
  17.241 +        }
  17.242 +        _count = index->count();
  17.243 +    }        
  17.244 +    
  17.245 +
  17.246 +
  17.247 +    Index::Iterator::Iterator (File *file, const Index *index)
  17.248 +        :_file(file),
  17.249 +         _current(index->table(-1)),
  17.250 +         _end(index->table(index->_tableSize))
  17.251 +    {
  17.252 +        next();
  17.253 +    }
  17.254 +    
  17.255 +    bool Index::Iterator::hasValue() const  {return _current != NULL;}
  17.256 +    Key Index::Iterator::key() const        {return Key(_current->value(_file)->key(), _current->hash());}
  17.257 +    Blob Index::Iterator::value() const     {return _current->value(_file)->value();}
  17.258 +    
  17.259 +    bool Index::Iterator::next() {
  17.260 +        if (_current) {
  17.261 +            while (++_current < _end) {
  17.262 +                if (_current->hasValue())
  17.263 +                    return true;
  17.264 +            }
  17.265 +            _current = NULL;
  17.266 +        }
  17.267 +        return false;
  17.268 +    }
  17.269 +    
  17.270 +    FilePosition Index::Iterator::valuePosition() {
  17.271 +        return _current ?_current->valuePosition() :0;
  17.272 +    }
  17.273 +}
    18.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    18.2 +++ b/src/MemoryMap.cpp	Sun Sep 20 15:14:12 2009 -0700
    18.3 @@ -0,0 +1,103 @@
    18.4 +/*
    18.5 + *  MemoryMap.cpp
    18.6 + *  Ottoman
    18.7 + *
    18.8 + *  Created by Jens Alfke on 9/17/09.
    18.9 + *  Copyright 2009 Jens Alfke. All rights reserved.
   18.10 + *  BSD-Licensed: See the file "LICENSE.txt" for details.
   18.11 + */
   18.12 +
   18.13 +#include "MemoryMap.h"
   18.14 +
   18.15 +#include <errno.h>
   18.16 +#include <stdio.h>
   18.17 +#include <sys/mman.h>
   18.18 +
   18.19 +namespace Mooseyard {
   18.20 +
   18.21 +    MemoryMap::~MemoryMap() {
   18.22 +        for (int i=0; i<_nRegions; i++)
   18.23 +            delete _regions[i];
   18.24 +        free(_regions);
   18.25 +    }
   18.26 +
   18.27 +    void MemoryMap::mapRegion (off_t pos, size_t length) {
   18.28 +        size_t end = pos+length;
   18.29 +        for (int i=0; i<_nRegions; i++) {
   18.30 +            Region *region = _regions[i];
   18.31 +            if (region->position() <= pos) {
   18.32 +                if (end <= region->end())
   18.33 +                    return;     // found an existing region covering this range
   18.34 +                else if (region->setLength(end - region->position()))
   18.35 +                    return;     // able to grow the existing region
   18.36 +            }
   18.37 +        }
   18.38 +        
   18.39 +        // No existing region, so add a new one:
   18.40 +        Region *region = new Region(_file, pos,length);
   18.41 +        _regions = (Region**) realloc(_regions, (_nRegions+1)*sizeof(*_regions));
   18.42 +        _regions[_nRegions++] = region;
   18.43 +    }
   18.44 +
   18.45 +    const void* MemoryMap::_at (off_t pos) const {
   18.46 +        for (int i=0; i<_nRegions; i++) {
   18.47 +            const void *ptr = _regions[i]->mappedPosition(pos);
   18.48 +            if (ptr)
   18.49 +                return ptr;
   18.50 +        }
   18.51 +        return NULL;
   18.52 +    }
   18.53 +    
   18.54 +    const void* MemoryMap::mappedPosition (off_t pos) const {
   18.55 +        const void *result = _at(pos);
   18.56 +        if (!result)
   18.57 +            throw File::Error("No memory mapped at this position");
   18.58 +        return result;
   18.59 +    }
   18.60 +
   18.61 +    
   18.62 +    
   18.63 +    
   18.64 +    MemoryMap::Region::Region (File* file, off_t position, size_t length)
   18.65 +    :_file(file),
   18.66 +    _position(position),
   18.67 +    _length (length),
   18.68 +    _start( (const uint8_t*) ::mmap(NULL, length, PROT_READ, MAP_PRIVATE, file->_fd, position) )
   18.69 +    {
   18.70 +        printf("File#%i: Mapped (%6llu -- %6llu) --> %p\n", file->_fd, _position, end(), _start);
   18.71 +        if (_start==NULL || _start == MAP_FAILED) {
   18.72 +            _start = NULL;
   18.73 +            throw File::Error(errno, strerror(errno));
   18.74 +        }
   18.75 +    }
   18.76 +    
   18.77 +    MemoryMap::Region::~Region() {
   18.78 +        if (_start) {
   18.79 +            printf("File#%i: Unmapped (%6llu -- %6llu) from %p\n", _file->_fd, _position, end(), _start);
   18.80 +            ::munmap((void*)_start,_length);
   18.81 +        }
   18.82 +    }
   18.83 +    
   18.84 +    bool MemoryMap::Region::setLength (size_t length) {
   18.85 +        if (length != _length) {
   18.86 +            printf("File#%i: Resiging (%6llu -- %6llu) from %lu to %lu ...", 
   18.87 +                   _file->_fd, _position, end(), _length,length);
   18.88 +            if (::mmap((void*)_start, length, PROT_READ, MAP_PRIVATE | MAP_FIXED, _file->_fd, _position) == MAP_FAILED) {
   18.89 +                printf("failed! errno=%i\n", errno);
   18.90 +                return false;
   18.91 +            }
   18.92 +            printf("OK\n");
   18.93 +            _length = length;
   18.94 +        }
   18.95 +        return true;
   18.96 +    }
   18.97 +    
   18.98 +    const void* MemoryMap::Region::mappedPosition (off_t pos) {
   18.99 +        if (pos >= _position && pos < end())
  18.100 +            return _start + (pos-_position);
  18.101 +        else
  18.102 +            return NULL;
  18.103 +    }
  18.104 +    
  18.105 +    
  18.106 +}
    19.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    19.2 +++ b/src/Ottoman.cpp	Sun Sep 20 15:14:12 2009 -0700
    19.3 @@ -0,0 +1,261 @@
    19.4 +/*
    19.5 + *  Ottoman.cpp
    19.6 + *  Ottoman
    19.7 + *
    19.8 + *  Created by Jens Alfke on 8/31/09.
    19.9 + *  Copyright 2009 Jens Alfke. All rights reserved.
   19.10 + *  BSD-Licensed: See the file "LICENSE.txt" for details.
   19.11 + */
   19.12 +
   19.13 +#include "Ottoman.h"
   19.14 +#include "VersionDictionary.h"
   19.15 +#include "Chunk.h"
   19.16 +#include "Index.h"
   19.17 +#include "File.h"
   19.18 +#include <fcntl.h>
   19.19 +#include <stdio.h>
   19.20 +#include <unistd.h>
   19.21 +
   19.22 +namespace Mooseyard {
   19.23 +    
   19.24 +    class DictionaryFileHeader :public Chunk {
   19.25 +    public:
   19.26 +        DictionaryFileHeader()                      
   19.27 +            :Chunk(sizeof(DictionaryFileHeader),kChunkType) 
   19.28 +        {
   19.29 +            memcpy(&magicNumber, &kMagicNumber, sizeof(magicNumber));
   19.30 +        }
   19.31 +        
   19.32 +        bool valid() const {
   19.33 +            return type()==kChunkType && memcmp(&magicNumber,&kMagicNumber,sizeof(magicNumber))==0;
   19.34 +        }
   19.35 +        
   19.36 +        uint8_t magicNumber[8];
   19.37 +        
   19.38 +        static const uint16_t kChunkType = 4;
   19.39 +        static const uint8_t kMagicNumber[8];
   19.40 +    };
   19.41 +
   19.42 +    const uint8_t DictionaryFileHeader::kMagicNumber[8] = {0x4A, 0x54, 0xF1, 0x1E, 'h', 'A', 's', 'H'};
   19.43 +
   19.44 +    
   19.45 +    
   19.46 +    Ottoman::Ottoman()
   19.47 +    :_writeable(true),
   19.48 +     _filename(NULL),
   19.49 +     _lastVersion(NULL),
   19.50 +     _current( new OverlayDictionary(&Dictionary::kEmpty) )
   19.51 +    {
   19.52 +    }
   19.53 +    
   19.54 +    Ottoman::Ottoman (const char *filename, bool writeable)
   19.55 +    :_writeable(writeable),
   19.56 +     _filename(strdup(filename)),
   19.57 +     _lastVersion(),
   19.58 +     _current()
   19.59 +    {
   19.60 +        _file = new File(_filename, writeable ?O_RDWR :O_RDONLY);
   19.61 +        _lastVersion = new VersionDictionary(_file);
   19.62 +        if (writeable)
   19.63 +            _current = new OverlayDictionary(_lastVersion);
   19.64 +    }
   19.65 +    
   19.66 +    Ottoman::~Ottoman() {
   19.67 +        delete _current;
   19.68 +        delete _lastVersion;
   19.69 +        delete _file;
   19.70 +        free(_filename);
   19.71 +    }
   19.72 +    
   19.73 +    
   19.74 +    bool Ottoman::needsSync() const {
   19.75 +        return _file && (!_lastVersion->isLatestVersion() || !_file->hasPath(_filename));
   19.76 +    }
   19.77 +    
   19.78 +    bool Ottoman::sync() {
   19.79 +        if (!needsSync())
   19.80 +            return false;
   19.81 +        File *curFile = _file;
   19.82 +        if (!curFile->hasPath(_filename))
   19.83 +            curFile = new File(_filename, _writeable ?O_RDWR :O_RDONLY);
   19.84 +        
   19.85 +        VersionDictionary *newest = new VersionDictionary(curFile);
   19.86 +        bool changed = (newest->generation() != _lastVersion->generation());
   19.87 +        if (changed) {
   19.88 +            if (newest->generation() < _lastVersion->generation())
   19.89 +                throw File::Error("Versions got lost in file update");
   19.90 +            
   19.91 +            if (_current)
   19.92 +                _current->rebase(newest);
   19.93 +            
   19.94 +            delete _lastVersion;
   19.95 +            _lastVersion = newest;
   19.96 +        }
   19.97 +        
   19.98 +        if (_file != curFile) {
   19.99 +            delete _file;
  19.100 +            _file = curFile;
  19.101 +        }
  19.102 +        return changed;
  19.103 +    }
  19.104 +    
  19.105 +    
  19.106 +    ChunkIterator* Ottoman::chunkIterator() const {
  19.107 +        return _file ?new ChunkIterator(_file, sizeof(DictionaryFileHeader)) :NULL;
  19.108 +    }
  19.109 +    
  19.110 +    bool Ottoman::scavenge (bool repair) {
  19.111 +        if (!_file)
  19.112 +            return true;
  19.113 +        
  19.114 +        const off_t actualEOF = _file->length();
  19.115 +        fprintf(stderr, "Ottoman::scavenge: Scanning %s (%llu / 0x%llX bytes) ...\n", 
  19.116 +                _filename, actualEOF, actualEOF);
  19.117 +        _file->setPosition(0);
  19.118 +        ChunkIterator it(_file, 0);
  19.119 +        const DictionaryFileHeader *header = (const DictionaryFileHeader *) it.chunk();
  19.120 +        if (!header || !header->valid()) {
  19.121 +            fprintf(stderr, "Ottoman::scavenge: No valid file header at all!\n");
  19.122 +            return false;
  19.123 +        }
  19.124 +        
  19.125 +        // Scan through all the chunks:
  19.126 +        FilePosition lastValidTrailer = 0;
  19.127 +        FilePosition validEOF = 0;
  19.128 +        int lastType = -1;
  19.129 +        int count = 0;
  19.130 +        try{
  19.131 +            for (; it; ++it) {
  19.132 +                const Chunk *chunk = it.chunk();
  19.133 +
  19.134 +                uint16_t type = it.chunk()->type();
  19.135 +                if (type != lastType) {
  19.136 +                    if (count > 0)
  19.137 +                        printf("%6d\n", count);
  19.138 +                    fprintf(stderr, "Ottoman::scavenge:    at 0x%08X: type %u ... ", 
  19.139 +                            it.position(), type);
  19.140 +                    lastType = type;
  19.141 +                    count = 0;
  19.142 +                }
  19.143 +                count++;
  19.144 +                
  19.145 +                switch (type) {
  19.146 +                    case KeyValueChunk::kChunkType:
  19.147 +                        ((const KeyValueChunk*)chunk)->validate();
  19.148 +                        break;
  19.149 +                    case Index::kChunkType:
  19.150 +                        ((const Index*)chunk)->validateEntries(_file);
  19.151 +                        break;
  19.152 +                    case VersionDictionary::kChunkType: {
  19.153 +                        fprintf(stderr, "Found dictionary trailer at %u!\n", it.position());
  19.154 +                        count = 0;
  19.155 +                        // Validate by instantiating the VersionDictionary:
  19.156 +                        VersionDictionary tempDict(_file, it.position());
  19.157 +                        // If constructor succeeded, it's valid, so remember it:
  19.158 +                        lastValidTrailer = it.position();
  19.159 +                        validEOF = lastValidTrailer + chunk->size();
  19.160 +                        fprintf(stderr, "Ottoman::scavenge:    Valid dictionary trailer at %X--%X\n",
  19.161 +                                lastValidTrailer, validEOF);
  19.162 +                        break;
  19.163 +                    }
  19.164 +                }
  19.165 +            }
  19.166 +            if (count > 0)
  19.167 +                fprintf(stderr, "%6d\n", count);
  19.168 +        } catch (File::Error &err) {
  19.169 +            fprintf(stderr, "Ottoman::scavenge caught File::Error(%i,\"%s\") at pos %llX\n",
  19.170 +                    err.code, err.message, _file->position());
  19.171 +            // Keep going; we can recover the dictionary(ies) before the bad one.
  19.172 +        }
  19.173 +        
  19.174 +        if (lastValidTrailer == 0) {
  19.175 +            fprintf(stderr, "Ottoman::scavenge: No valid dictionaries found!\n");
  19.176 +            return false;
  19.177 +        } else if (validEOF < actualEOF) {
  19.178 +            fprintf(stderr, "Ottoman::scavenge: Need to truncate to 0x%X (0x%llX bytes)\n",
  19.179 +                    lastValidTrailer, actualEOF-lastValidTrailer);
  19.180 +            if (repair) {
  19.181 +                _file->setLength(validEOF);
  19.182 +                _file->flushDisk();
  19.183 +            }
  19.184 +            return false;
  19.185 +        }
  19.186 +        
  19.187 +        fprintf(stderr, "Ottoman::scavenge: File is OK!\n");
  19.188 +        return true;
  19.189 +    }
  19.190 +    
  19.191 +    
  19.192 +#pragma mark -
  19.193 +#pragma mark SAVING:
  19.194 +    
  19.195 +    
  19.196 +    // low-level write. Does not lock or check for conflicts!
  19.197 +    void Ottoman::_append (File *dstFile) {
  19.198 +        VersionDictionary *lastVersion = _lastVersion;
  19.199 +        if (!lastVersion)
  19.200 +            lastVersion = new VersionDictionary(dstFile);
  19.201 +        VersionDictionary *saved = lastVersion->_appendAndOpen(_current->overlay(), 
  19.202 +                                                            dstFile,
  19.203 +                                                            _current->baseReplaced());
  19.204 +        // (don't delete _lastVersion: saved->_previousVersion now points to it.)
  19.205 +        _lastVersion = saved;
  19.206 +        _current->revertTo(_lastVersion);
  19.207 +    }
  19.208 +    
  19.209 +    bool Ottoman::save() {
  19.210 +        if (!_file)
  19.211 +            return false;
  19.212 +        if (_current && _current->isChanged()) {
  19.213 +            File::Lock lock(_file);
  19.214 +            if (needsSync())
  19.215 +                return false;       // conflict!
  19.216 +            _append(_file);
  19.217 +        }
  19.218 +        return true;
  19.219 +    }
  19.220 +    
  19.221 +    File* Ottoman::_writeTo (const char *dstFileName, bool overwriteAllowed) {
  19.222 +        int mode = O_RDWR | O_CREAT | O_TRUNC | O_EXLOCK;
  19.223 +        if (!overwriteAllowed)
  19.224 +            mode |= O_EXCL;
  19.225 +        File *dstFile = new File(dstFileName, mode);
  19.226 +        try {
  19.227 +            dstFile->write(DictionaryFileHeader());
  19.228 +            _append(dstFile);
  19.229 +        } catch (...) {
  19.230 +            delete dstFile;
  19.231 +            File::unlink(dstFileName);
  19.232 +            throw;
  19.233 +        }
  19.234 +        return dstFile;
  19.235 +    }
  19.236 +    
  19.237 +    void Ottoman::saveAs (const char *dstFileName, bool overwriteAllowed) {
  19.238 +        File *dstFile = _writeTo(dstFileName, overwriteAllowed);
  19.239 +        free(_filename);
  19.240 +        _filename = strdup(dstFileName);
  19.241 +        _file = dstFile;
  19.242 +    }
  19.243 +
  19.244 +    bool Ottoman::saveAndCompact() {
  19.245 +        if (!_file)
  19.246 +            return false;
  19.247 +        char tempFileName[1024];
  19.248 +        strlcpy(tempFileName, _filename, sizeof(tempFileName)-1);
  19.249 +        strlcat(tempFileName, "~", sizeof(tempFileName));
  19.250 +        File *tempFile;
  19.251 +        {
  19.252 +            // Check for conflict in existing file:
  19.253 +            File::Lock lock(_file);
  19.254 +            if (needsSync())
  19.255 +                return false;
  19.256 +            tempFile = _writeTo(tempFileName, false);
  19.257 +            File::rename(tempFileName, _filename);
  19.258 +        }
  19.259 +        delete _file;
  19.260 +        _file = tempFile;
  19.261 +        return true;
  19.262 +    }
  19.263 +
  19.264 +}
    20.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    20.2 +++ b/src/VersionDictionary.cpp	Sun Sep 20 15:14:12 2009 -0700
    20.3 @@ -0,0 +1,429 @@
    20.4 +/*
    20.5 + *  VersionDictionary.cpp
    20.6 + *  Ottoman
    20.7 + *
    20.8 + *  Created by Jens Alfke on 8/21/09.
    20.9 + *  Copyright 2009 Jens Alfke. All rights reserved.
   20.10 + *  BSD-Licensed: See the file "LICENSE.txt" for details.
   20.11 + */
   20.12 +
   20.13 +#include "VersionDictionary.h"
   20.14 +#include "Chunk.h"
   20.15 +#include "Index.h"
   20.16 +#include "File.h"
   20.17 +#include "Chunk.h"
   20.18 +#include <assert.h>
   20.19 +#include <errno.h>
   20.20 +#include <algorithm>
   20.21 +
   20.22 +namespace Mooseyard {
   20.23 +            
   20.24 +    // Trailer is stored in file; all numbers are little-endian.
   20.25 +    class VersionDictionary::Trailer :public Chunk {
   20.26 +    public:
   20.27 +        LittleEndian<uint32_t>          magicNumber1;
   20.28 +        LittleEndian<uint32_t>          count;
   20.29 +        VersionDictionary::IndexPositions  indexPositions;
   20.30 +        LittleEndian<FilePosition>      previousTrailerPosition;
   20.31 +        LittleEndian<uint32_t>          generation;
   20.32 +        LittleEndianDouble              timestamp;
   20.33 +        LittleEndian<uint32_t>          magicNumber2;
   20.34 +                
   20.35 +        Trailer ()
   20.36 +            :Chunk(sizeof(Trailer), kChunkType)
   20.37 +        { }
   20.38 +        
   20.39 +        Trailer (int c,
   20.40 +                     VersionDictionary::IndexPositions indexPos, 
   20.41 +                     FilePosition previousTrailerPos,
   20.42 +                     uint32_t gen)
   20.43 +            :Chunk(sizeof(Trailer), kChunkType),
   20.44 +             magicNumber1(kMagicNumber1),
   20.45 +             count(c),
   20.46 +             indexPositions(indexPos),
   20.47 +             previousTrailerPosition(previousTrailerPos),
   20.48 +             generation(gen),
   20.49 +             timestamp(::time(NULL)),
   20.50 +             magicNumber2(kMagicNumber2)
   20.51 +        { }
   20.52 +        
   20.53 +        static const uint32_t kMagicNumber1 = 0xfe83b1cd;
   20.54 +        static const uint32_t kMagicNumber2 = 0xff84b2c1;
   20.55 +    };
   20.56 +    
   20.57 +    
   20.58 +    
   20.59 +    VersionDictionary::VersionDictionary (File *file)
   20.60 +        :_file(file),
   20.61 +         _trailerPosition(0),
   20.62 +         _previousTrailerPosition(0),
   20.63 +         _indexPositions(),
   20.64 +         _count(0),
   20.65 +         _previousVersion()
   20.66 +    {
   20.67 +        if (file->length() > sizeof(Trailer))
   20.68 +            _read();
   20.69 +    }
   20.70 +    
   20.71 +    VersionDictionary::VersionDictionary (File *file, FilePosition trailerPosition)
   20.72 +        :_file(file),
   20.73 +         _trailerPosition(trailerPosition),
   20.74 +         _previousTrailerPosition(0),
   20.75 +         _indexPositions(),
   20.76 +         _count(0),
   20.77 +         _previousVersion()
   20.78 +    {
   20.79 +        _read(trailerPosition);
   20.80 +    }
   20.81 +    
   20.82 +    int VersionDictionary::count() const {
   20.83 +        return _count;
   20.84 +    }
   20.85 +    
   20.86 +    const VersionDictionary::Trailer* VersionDictionary::_trailer() const {
   20.87 +        if (_trailerPosition > 0)
   20.88 +            return (const VersionDictionary::Trailer*) _file->mappedPosition(_trailerPosition);
   20.89 +        else 
   20.90 +            return NULL;      // Only happens in the empty-file case
   20.91 +    }
   20.92 +    
   20.93 +    const Index* VersionDictionary::_index (int i) const {
   20.94 +        if (_indexPositions[i] > 0)
   20.95 +            return (const Index*) _file->mappedPosition(_indexPositions[i]);
   20.96 +        else
   20.97 +            return NULL;
   20.98 +    }
   20.99 +    
  20.100 +    int VersionDictionary::generation() const {
  20.101 +        const VersionDictionary::Trailer *trailer = _trailer();
  20.102 +        return trailer ? (int)trailer->generation : -1;
  20.103 +    }
  20.104 +    
  20.105 +    time_t VersionDictionary::timestamp() const {
  20.106 +        const VersionDictionary::Trailer *trailer = _trailer();
  20.107 +        return trailer ? (time_t)trailer->timestamp : 0;
  20.108 +    }
  20.109 +    
  20.110 +    const VersionDictionary* VersionDictionary::previousVersion() const {
  20.111 +        if (!_previousVersion)
  20.112 +            if (_previousTrailerPosition > 0)
  20.113 +                _previousVersion = new VersionDictionary(_file, _previousTrailerPosition);
  20.114 +        return _previousVersion;
  20.115 +    }
  20.116 +        
  20.117 +    
  20.118 +    Blob VersionDictionary::get (Key key) const {
  20.119 +        const Index *index = _index(key.hash >> 24);
  20.120 +        return index ?index->get(_file, key) :Blob();
  20.121 +    }
  20.122 +    
  20.123 +    void VersionDictionary::_read (FilePosition trailerPos) {
  20.124 +        assert(_file);
  20.125 +        
  20.126 +        if (trailerPos > 0) {
  20.127 +            _file->setPosition(trailerPos);
  20.128 +        } else {
  20.129 +            // Determine position of trailer, at EOF:
  20.130 +            off_t pos = _file->setPositionToEnd(sizeof(VersionDictionary::Trailer));
  20.131 +            if (pos < 0 || (pos & 0x03) || pos > UINT32_MAX)
  20.132 +                throw File::Error(ERANGE, "No trailer found in file (wrong EOF)");
  20.133 +            trailerPos = pos;
  20.134 +        }
  20.135 +        
  20.136 +        // Read & verify trailer:
  20.137 +        VersionDictionary::Trailer trailer;
  20.138 +        _file->read(trailer);
  20.139 +        _trailerPosition = trailerPos;
  20.140 +        _count = trailer.count;
  20.141 +        _indexPositions = trailer.indexPositions;
  20.142 +        
  20.143 +        if (trailer.magicNumber1 != VersionDictionary::Trailer::kMagicNumber1
  20.144 +            || trailer.magicNumber2 != VersionDictionary::Trailer::kMagicNumber2)
  20.145 +            throw File::Error(ERANGE, "No trailer found in file (invalid magic numbers)");\
  20.146 +
  20.147 +        
  20.148 +        // Map in the file:
  20.149 +        _file->mapRegion(0, _trailerPosition+sizeof(trailer));
  20.150 +
  20.151 +        // Verify Indexes:
  20.152 +        for (int i=0; i<256; i++) {
  20.153 +            const Index *index = _index(i);
  20.154 +            if (index)
  20.155 +                index->validate();
  20.156 +        }
  20.157 +}
  20.158 +    
  20.159 +    
  20.160 +    bool VersionDictionary::isLatestVersion() const {
  20.161 +        return _file->length() <= _trailerPosition+sizeof(VersionDictionary::Trailer);
  20.162 +    }
  20.163 +    
  20.164 +    
  20.165 +    /*  Append addDict to baseDict, writing the results into dstFile (which is usually the same
  20.166 +        as baseDict->file().) If 'replace' is true, ignore the old contents of baseDict.
  20.167 +        Returns the position of the new trailer. */
  20.168 +    FilePosition VersionDictionary::_append (const VersionDictionary *baseDict, 
  20.169 +                                          const Dictionary *addDict, 
  20.170 +                                          File *dstFile, 
  20.171 +                                          bool replace) 
  20.172 +    {
  20.173 +        File *srcFile = baseDict->_file;
  20.174 +        bool incremental = !replace && dstFile==srcFile;
  20.175 +        Index* newIndex[256];
  20.176 +
  20.177 +        {
  20.178 +            // Work out the needed capacity for each Index bucket:
  20.179 +            int newCounts[256] = {0};
  20.180 +            if (!replace) {
  20.181 +                for (int i=0; i<256; i++) {
  20.182 +                    const Index *oldIndex = baseDict->_index(i);
  20.183 +                    if (oldIndex)
  20.184 +                        newCounts[i] = oldIndex->count();
  20.185 +                }
  20.186 +            }
  20.187 +            Dictionary::Iterator *it = addDict->iterate();
  20.188 +            for (; *it; ++*it)
  20.189 +                newCounts[it->key().hash >> 24]++;
  20.190 +            delete it;
  20.191 +            
  20.192 +            // Allocate new Indexes, of sufficient capacity, for each growing bucket:
  20.193 +            for (int i=0; i<256; i++) {
  20.194 +                const Index *oldIndex = baseDict->_index(i);
  20.195 +                if (newCounts[i] && (!incremental || !oldIndex || newCounts[i] > oldIndex->count())) {
  20.196 +                    newIndex[i] = Index::create(newCounts[i]);
  20.197 +                    if (incremental && oldIndex)
  20.198 +                        newIndex[i]->copyFrom(srcFile, oldIndex);
  20.199 +                } else
  20.200 +                    newIndex[i] = NULL;
  20.201 +            }
  20.202 +        }
  20.203 +        
  20.204 +        // Lock the file now, seek to the end, and make sure it's been prepared with a header,
  20.205 +        // since FilePositions of 0 and 1 are reserved.
  20.206 +        File::Lock lock(dstFile);
  20.207 +        const FilePosition startPos = dstFile->setPositionToEnd();
  20.208 +        if (startPos <= 1)
  20.209 +            throw File::Error(ERANGE, "Cannot write VersionDictionary to empty file");
  20.210 +        
  20.211 +        // For safety's sake, make sure the old file hasn't been destroyed:
  20.212 +        FilePosition oldEOF = baseDict->_trailerPosition;
  20.213 +        if (oldEOF > 0)
  20.214 +            oldEOF += sizeof(VersionDictionary::Trailer);
  20.215 +        if (srcFile->length() < oldEOF)
  20.216 +            throw File::Error(ERANGE, "File has been truncated since being read");
  20.217 +            
  20.218 +        try{
  20.219 +            FilePosition pos = startPos;
  20.220 +            
  20.221 +            int newCount;
  20.222 +            if (replace)
  20.223 +                newCount = 0;
  20.224 +            else if (dstFile == srcFile)
  20.225 +                newCount = baseDict->_count;
  20.226 +            else {
  20.227 +                // Write out the surviving pre-existing entries from the old file:
  20.228 +                newCount = 0;
  20.229 +                for (VersionDictionary::Iterator it(baseDict); it; ++it) {
  20.230 +                    Key key = it.key();
  20.231 +                    if (!addDict->contains(key)) {
  20.232 +                        int bucket = key.hash >> 24;
  20.233 +                        int size = KeyValueChunk::write(dstFile,pos, key, it.value());
  20.234 +                        newIndex[bucket]->put(srcFile, key, pos, startPos);
  20.235 +                        pos += size;
  20.236 +                        newCount++;
  20.237 +                    }
  20.238 +                }
  20.239 +            }
  20.240 +            
  20.241 +            // Now write the items from the new dict:
  20.242 +            Dictionary::Iterator *it = addDict->iterate();
  20.243 +            for (; *it; ++*it) {
  20.244 +                Key key=it->key();
  20.245 +                Blob value=it->value();
  20.246 +                int bucket = key.hash >> 24;
  20.247 +                Index *index = newIndex[bucket];
  20.248 +                assert(index);
  20.249 +                                              
  20.250 +                if (value.bytes) {
  20.251 +                    int size = KeyValueChunk::write(dstFile,pos, key, value);
  20.252 +                    if (index->put(srcFile, key, pos, startPos))
  20.253 +                        newCount++;
  20.254 +                    pos += size;
  20.255 +                } else if (incremental) {
  20.256 +                    // NULL value is a deleted-entry marker used by OverlayDictionary
  20.257 +                    if (index->remove(srcFile, key, startPos))
  20.258 +                        newCount--;
  20.259 +                }
  20.260 +            }
  20.261 +            delete it;
  20.262 +            
  20.263 +            // Write out the new indexes:
  20.264 +            IndexPositions newIndexPositions;
  20.265 +            if (incremental)
  20.266 +                newIndexPositions = baseDict->_indexPositions;
  20.267 +            else
  20.268 +                memset(&newIndexPositions, 0, sizeof(newIndexPositions));
  20.269 +
  20.270 +            pos += Chunk::writePadding(dstFile);
  20.271 +            for (int i=0; i<256; i++) {
  20.272 +                if (newIndex[i]) {
  20.273 +                    Index *index = newIndex[i];
  20.274 +                    newIndexPositions[i] = pos;
  20.275 +                    pos += dstFile->write(index, index->size());
  20.276 +                    delete index;
  20.277 +                }
  20.278 +            }
  20.279 +            
  20.280 +            // Flush everything out to disk, with maximum paranoia, before writing the trailer.
  20.281 +            // Since scavenging corrupt files looks for trailers, we don't want to append a
  20.282 +            // trailer until we're certain that all of the real data is safely on-disk.
  20.283 +            dstFile->flushDisk();
  20.284 +            
  20.285 +            // Write the trailer:
  20.286 +            FilePosition newTrailerPosition = pos;
  20.287 +            VersionDictionary::Trailer trailer(newCount,
  20.288 +                                            newIndexPositions,
  20.289 +                                            baseDict->_trailerPosition,
  20.290 +                                            baseDict->generation() + 1);
  20.291 +            pos += dstFile->write(trailer);
  20.292 +            
  20.293 +            // Just a mild flush here; flushDisk() is very expensive and can be disruptive to
  20.294 +            // real-time I/O in other apps, so it's bad to call it too often.
  20.295 +            dstFile->flush();
  20.296 +            assert(pos==dstFile->position());
  20.297 +            
  20.298 +            return newTrailerPosition;
  20.299 +            
  20.300 +        } catch (...) {
  20.301 +            // If something goes wrong, try to back out everything we wrote:
  20.302 +            try{
  20.303 +                dstFile->setLength(startPos);
  20.304 +            }catch(...) { }
  20.305 +            throw;
  20.306 +        }
  20.307 +    }
  20.308 + 
  20.309 +    
  20.310 +#pragma mark -
  20.311 +#pragma mark TESTING-ONLY:
  20.312 +    
  20.313 +    VersionDictionary* VersionDictionary::_appendAndOpen (const Dictionary *addDict,
  20.314 +                                                    File *dstFile, 
  20.315 +                                                    bool replace) const
  20.316 +    {
  20.317 +        FilePosition nextVersionPos = _append(this, addDict, dstFile, replace);
  20.318 +        VersionDictionary *nextVersion = new VersionDictionary(dstFile, nextVersionPos);
  20.319 +        nextVersion->_previousVersion = this;
  20.320 +        return nextVersion;
  20.321 +    }
  20.322 +    
  20.323 +    VersionDictionary* VersionDictionary::create (File *file, const Dictionary *srcDict) {
  20.324 +        return VersionDictionary(file)._appendAndOpen(srcDict, file, true);
  20.325 +    }
  20.326 +    
  20.327 +    
  20.328 +#pragma mark -
  20.329 +#pragma mark ITERATOR:
  20.330 +    
  20.331 +    Dictionary::Iterator* VersionDictionary::iterate() const {
  20.332 +        return new VersionDictionary::Iterator(this);
  20.333 +    }
  20.334 +    
  20.335 +    VersionDictionary::Iterator::Iterator (const VersionDictionary *file)
  20.336 +    :_file(file),
  20.337 +     _bucket(-1),
  20.338 +     _iter(NULL)
  20.339 +    { 
  20.340 +        nextIndex();
  20.341 +    }
  20.342 +    
  20.343 +    VersionDictionary::Iterator::~Iterator() {
  20.344 +        delete _iter;
  20.345 +    }
  20.346 +    
  20.347 +    bool VersionDictionary::Iterator::hasValue() const               {return _iter && _iter->hasValue();}
  20.348 +    Key VersionDictionary::Iterator::key() const                     {return _iter->key();}
  20.349 +    Blob VersionDictionary::Iterator::value() const                  {return _iter->value();}
  20.350 +    
  20.351 +    bool VersionDictionary::Iterator::next() {
  20.352 +        if (_iter->next())
  20.353 +            return true;
  20.354 +        else {
  20.355 +            delete _iter;
  20.356 +            return nextIndex();
  20.357 +        }
  20.358 +    }
  20.359 +    
  20.360 +    bool VersionDictionary::Iterator::nextIndex() {
  20.361 +        while (++_bucket < 256) {
  20.362 +            const Index *index = _file->_index(_bucket);
  20.363 +            if (index) {
  20.364 +                _iter = new Index::Iterator(_file->_file, index);
  20.365 +                if (*_iter)
  20.366 +                    return true;
  20.367 +                else
  20.368 +                    delete _iter;
  20.369 +            }
  20.370 +        }
  20.371 +        _iter = NULL;
  20.372 +        return false;
  20.373 +    }
  20.374 +    
  20.375 +    
  20.376 +#pragma mark -
  20.377 +#pragma mark CHANGE ITERATOR:
  20.378 +    
  20.379 +    VersionDictionary::ChangeIterator* VersionDictionary::iterateChanges() const {
  20.380 +        return new VersionDictionary::ChangeIterator(this);
  20.381 +    }
  20.382 +    
  20.383 +    VersionDictionary::ChangeIterator::ChangeIterator (const VersionDictionary *file)
  20.384 +    :_file(file),
  20.385 +     _bucket(-1),
  20.386 +     _iter(NULL)
  20.387 +    { 
  20.388 +        next();            
  20.389 +    }
  20.390 +    
  20.391 +    VersionDictionary::ChangeIterator::~ChangeIterator() {
  20.392 +        delete _iter;
  20.393 +    }
  20.394 +    
  20.395 +    bool VersionDictionary::ChangeIterator::hasValue() const               {return _iter && _iter->hasValue();}
  20.396 +    Key VersionDictionary::ChangeIterator::key() const                     {return _iter->key();}
  20.397 +    Blob VersionDictionary::ChangeIterator::value() const                  {return _iter->value();}
  20.398 +    
  20.399 +    bool VersionDictionary::ChangeIterator::next() {
  20.400 +        const VersionDictionary::Trailer *trailer = _file->_trailer();
  20.401 +        for (;;) {
  20.402 +            // Check if current iterator has a value that's from this version:
  20.403 +            if (_iter && _iter->hasValue()) {
  20.404 +                if (((Index::Iterator*)_iter)->valuePosition() > trailer->previousTrailerPosition)
  20.405 +                    return true;
  20.406 +                else if (_iter->next())
  20.407 +                    continue;
  20.408 +            }
  20.409 +            // If not, go to next Index:
  20.410 +            if (!nextIndex())
  20.411 +                return false;
  20.412 +        }
  20.413 +    }
  20.414 +    
  20.415 +    bool VersionDictionary::ChangeIterator::nextIndex() {
  20.416 +        delete _iter;
  20.417 +        const VersionDictionary::Trailer *trailer = _file->_trailer();
  20.418 +        while (++_bucket < 256) {
  20.419 +            const Index *index = _file->_index(_bucket);
  20.420 +            if (index) {
  20.421 +                // Skip indexes that weren't updated in this version:
  20.422 +                if (trailer->indexPositions[_bucket] > trailer->previousTrailerPosition) {
  20.423 +                    _iter = new Index::Iterator(_file->_file, index);
  20.424 +                    return true;
  20.425 +                }
  20.426 +            }
  20.427 +        }
  20.428 +        _iter = NULL;
  20.429 +        return false;
  20.430 +    }
  20.431 +    
  20.432 +}
    21.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    21.2 +++ b/test/Dictionary_test.cpp	Sun Sep 20 15:14:12 2009 -0700
    21.3 @@ -0,0 +1,111 @@
    21.4 +/*
    21.5 + *  Dictionary_test.cpp
    21.6 + *  Ottoman
    21.7 + *
    21.8 + *  Created by Jens Alfke on 9/4/09.
    21.9 + *  Copyright 2009 Jens Alfke. All rights reserved.
   21.10 + *  BSD-Licensed: See the file "LICENSE.txt" for details.
   21.11 + */
   21.12 +
   21.13 +
   21.14 +#include <gtest/gtest.h>
   21.15 +#include "TestUtils.h"
   21.16 +#include "File.h"
   21.17 +#include "Dictionary.h"
   21.18 +#include "Hash.h"
   21.19 +#include <fcntl.h>
   21.20 +#include <stdio.h>
   21.21 +
   21.22 +using namespace Mooseyard;
   21.23 +
   21.24 +static const HashDictionary& getDict() {
   21.25 +    static HashDictionary *sDict;
   21.26 +    if (!sDict) {
   21.27 +        printf("Building large HashDictionary...\n");
   21.28 +        sDict = new HashDictionary();
   21.29 +        readWords();
   21.30 +        for( int i=0; i<sNWords; i++) {
   21.31 +            Blob kv(sWords[i]);
   21.32 +            sDict->put(Key(kv),kv);
   21.33 +        }
   21.34 +    }
   21.35 +    return *sDict;
   21.36 +}
   21.37 +
   21.38 +TEST(Dictionary,GetAll) {
   21.39 +    const Dictionary &dict = getDict();
   21.40 +    EXPECT_EQ( sNWords ,  dict.count() );
   21.41 +    for( int i=0; i<sNWords; i++) {
   21.42 +        Key key(sWords[i]);
   21.43 +        Blob value = dict.get(key);
   21.44 +        EXPECT_TRUE(value);
   21.45 +        EXPECT_TRUE( value.equals(key) );
   21.46 +    }
   21.47 +}
   21.48 +
   21.49 +TEST(Dictionary,Iterate) {
   21.50 +    const HashDictionary &dict = getDict();
   21.51 +    Timer t("Iterating Dictionary", sNWords);
   21.52 +    int n=0;
   21.53 +    for( HashDictionary::Iterator it(dict); it; ++it) {
   21.54 +        n++;
   21.55 +        EXPECT_TRUE(it.key().length > 0 && it.key().length < 50);
   21.56 +        EXPECT_TRUE(it.key().equals(it.value()));
   21.57 +        EXPECT_EQ( 0, ((size_t)it.value().bytes & 0x3) );  // 4-byte aligned
   21.58 +    }
   21.59 +    EXPECT_EQ(sNWords, n);
   21.60 +}
   21.61 +
   21.62 +TEST(Dictionary,Overlay) {
   21.63 +    const Dictionary &dict = getDict();
   21.64 +    OverlayDictionary overlay(&dict);
   21.65 +    
   21.66 +    EXPECT_EQ( sNWords ,  overlay.count() );
   21.67 +    EXPECT_TRUE(overlay.get("animal").equals("animal"));
   21.68 +    EXPECT_TRUE(overlay.get("asparagus").equals("asparagus"));
   21.69 +    EXPECT_FALSE(overlay.get("growf"));
   21.70 +    
   21.71 +    overlay.put("animal", "AMINAL");
   21.72 +    overlay.put("growf", "growf");
   21.73 +    EXPECT_TRUE(overlay.remove("asparagus"));
   21.74 +    
   21.75 +    EXPECT_EQ( sNWords, overlay.count() );
   21.76 +    EXPECT_TRUE(overlay.get("animal").equals("AMINAL"));
   21.77 +    EXPECT_TRUE(overlay.get("growf").equals("growf"));
   21.78 +    EXPECT_TRUE(overlay.contains("growf"));
   21.79 +    EXPECT_FALSE(overlay.get("asparagus"));
   21.80 +    EXPECT_FALSE(overlay.contains("asparagus"));
   21.81 +    
   21.82 +    int n=0;
   21.83 +    for( OverlayDictionary::Iterator it(overlay); it; ++it) {
   21.84 +        n++;
   21.85 +        EXPECT_TRUE(!it.key().equals("asparagus"));
   21.86 +    }
   21.87 +    EXPECT_EQ(sNWords, n);
   21.88 +    
   21.89 +    printf("Testing ChangeIterator...\n");
   21.90 +    n=0;
   21.91 +    int foundAsparagus=0, foundAnimal=0, foundGrowf=0;
   21.92 +    for( Dictionary::ChangeIterator it(&overlay); it; ++it) {
   21.93 +        n++;
   21.94 +        if (it.key().equals("animal")) {
   21.95 +            foundAnimal++;
   21.96 +            EXPECT_TRUE(it.value().equals("AMINAL"));
   21.97 +            EXPECT_TRUE(it.otherValue().equals("animal"));
   21.98 +        } else if (it.key().equals("asparagus")) {
   21.99 +            foundAsparagus++;
  21.100 +            EXPECT_FALSE(it.value());
  21.101 +            EXPECT_TRUE(it.otherValue().equals("asparagus"));
  21.102 +        } else if (it.key().equals("growf")) {
  21.103 +            foundGrowf++;
  21.104 +            EXPECT_TRUE(it.value().equals("growf"));
  21.105 +            EXPECT_FALSE(it.otherValue());
  21.106 +        } else {
  21.107 +            EXPECT_TRUE(false);
  21.108 +        }
  21.109 +    }
  21.110 +    EXPECT_EQ(1, foundAnimal);
  21.111 +    EXPECT_EQ(1, foundAsparagus);
  21.112 +    EXPECT_EQ(1, foundGrowf);
  21.113 +    EXPECT_EQ(3, n);
  21.114 +}
    22.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    22.2 +++ b/test/Hash_test.cpp	Sun Sep 20 15:14:12 2009 -0700
    22.3 @@ -0,0 +1,193 @@
    22.4 +/*
    22.5 + *  Hash_test.cpp
    22.6 + *  Ottoman
    22.7 + *
    22.8 + *  Created by Jens Alfke on 9/2/09.
    22.9 + *  Copyright 2009 Jens Alfke. All rights reserved.
   22.10 + *  BSD-Licensed: See the file "LICENSE.txt" for details.
   22.11 + */
   22.12 +
   22.13 +#include <gtest/gtest.h>
   22.14 +#include "TestUtils.h"
   22.15 +#include "File.h"
   22.16 +#include "Hash.h"
   22.17 +#include <stdio.h>
   22.18 +
   22.19 +using namespace Mooseyard;
   22.20 +
   22.21 +
   22.22 +static bool verbose = false;
   22.23 +static int num = 100;
   22.24 +
   22.25 +
   22.26 +#pragma mark -
   22.27 +#pragma mark UTILITIES:
   22.28 +
   22.29 +
   22.30 +char* mkKey (int i) {
   22.31 +    static char* sKeys[100];
   22.32 +    if (i>=0 && i<100 && sKeys[i] != NULL)
   22.33 +        return sKeys[i];
   22.34 +    char str[10];
   22.35 +    sprintf(str, "%i",i);
   22.36 +    char *key = strdup(str);
   22.37 +    if (i>=0 && i<100)
   22.38 +        sKeys[i] = key;
   22.39 +    return key;
   22.40 +}
   22.41 +
   22.42 +static char* Insert(Hash &hash, const char* key) {
   22.43 +    char string[100];
   22.44 +    sprintf(string, "value for %s", key);
   22.45 +    if (verbose)
   22.46 +        fprintf(stderr, "\nInserting %s -> '%s'\n", key,string);
   22.47 +    int count = hash.count();
   22.48 +    char *leaf = strdup(string);
   22.49 +    
   22.50 +    hash.put(key,leaf);
   22.51 +    
   22.52 +    if (verbose)
   22.53 +        hash.dump();
   22.54 +    EXPECT_EQ( count+1, hash.count() );
   22.55 +    EXPECT_EQ( leaf, hash.get(key) );
   22.56 +    return leaf;
   22.57 +}
   22.58 +
   22.59 +static char* Insert(Hash &hash, int i) {
   22.60 +    return Insert(hash, mkKey(i));
   22.61 +}
   22.62 +
   22.63 +static void Remove(Hash &hash, int i) {
   22.64 +    char* key = mkKey(i);
   22.65 +    char string[100];
   22.66 +    sprintf(string, "value for %s", key);
   22.67 +    if (verbose)
   22.68 +        fprintf(stderr, "\nRemoving %s\n", key);
   22.69 +    int count = hash.count();
   22.70 +    char *value = (char*) hash.get(key);
   22.71 +    EXPECT_TRUE(value!=NULL);
   22.72 +    EXPECT_EQ(0, strcmp(value,string));
   22.73 +
   22.74 +    EXPECT_TRUE(hash.remove(key));
   22.75 +    
   22.76 +    if (verbose)
   22.77 +        hash.dump();
   22.78 +    EXPECT_EQ( count-1, hash.count() );
   22.79 +    EXPECT_EQ( NULL, hash.get(key) );
   22.80 +}
   22.81 +
   22.82 +
   22.83 +
   22.84 +#pragma mark -
   22.85 +#pragma mark TESTS:
   22.86 +
   22.87 +
   22.88 +TEST(Hash,Simple) {
   22.89 +    Hash hash;
   22.90 +    hash.dump();
   22.91 +    EXPECT_EQ(0, hash.count());
   22.92 +    
   22.93 +    char *seventeen = Insert(hash, "seventeen");
   22.94 +    
   22.95 +    EXPECT_EQ(NULL, hash.get("zero"));
   22.96 +    EXPECT_EQ(NULL, hash.get("eighteen"));
   22.97 +    
   22.98 +    char *four = Insert(hash, "four");
   22.99 +    EXPECT_EQ(seventeen, hash.get("seventeen"));
  22.100 +    
  22.101 +    char *zero = Insert(hash, "zero");
  22.102 +    char *hundred = Insert(hash, "one hundred");
  22.103 +    char *eight = Insert(hash, "eight");
  22.104 +    char *twenty = Insert(hash, "twenty");
  22.105 +    
  22.106 +    EXPECT_EQ(zero, hash.get("zero"));
  22.107 +    EXPECT_EQ(four, hash.get("four"));
  22.108 +    EXPECT_EQ(eight, hash.get("eight"));
  22.109 +    EXPECT_EQ(seventeen, hash.get("seventeen"));
  22.110 +    EXPECT_EQ(twenty, hash.get("twenty"));
  22.111 +    EXPECT_EQ(hundred, hash.get("one hundred"));
  22.112 +    
  22.113 +    int i=0;
  22.114 +    for (Hash::Iterator iter(&hash); iter; ++iter, ++i) {
  22.115 +        Blob key = iter.key();
  22.116 +        if (verbose)
  22.117 +            fprintf(stderr, "   %*s -> %s\n", 
  22.118 +                    (int)key.length,(const char*)key.bytes,
  22.119 +                    ((char*)iter.value()));
  22.120 +    }
  22.121 +    EXPECT_EQ(6, i);
  22.122 +}
  22.123 +
  22.124 +TEST(Hash,InsertLotsAtRandom) {
  22.125 +    int map[num];
  22.126 +    for (int i=0; i<num; i++)
  22.127 +        map[i] = i;
  22.128 +    shuffle(map,num);
  22.129 +    if (verbose) {
  22.130 +        for (int i=0; i<num; i++)
  22.131 +            fprintf(stderr, "%3d ",map[i]);
  22.132 +        fprintf(stderr, "\n");
  22.133 +    }
  22.134 +    
  22.135 +    Hash hash(8);
  22.136 +    char* expected[num];
  22.137 +    memset(&expected,0,sizeof(expected));
  22.138 +    for (int i=0; i<num; i++) {
  22.139 +        expected[map[i]] = Insert(hash, map[i]);
  22.140 +        for (int j=0; j<num; j++)
  22.141 +            EXPECT_EQ(expected[map[j]], hash.get(mkKey(map[j])));
  22.142 +    }
  22.143 +}
  22.144 +
  22.145 +TEST(Hash,DeleteAtRandom) {
  22.146 +    Hash hash;
  22.147 +    for (int i=0; i<num; i++)
  22.148 +        Insert(hash,i);
  22.149 +
  22.150 +    int map[num];
  22.151 +    for (int i=0; i<num; i++)
  22.152 +        map[i] = i;
  22.153 +    shuffle(map,num);
  22.154 +    if (verbose) {
  22.155 +        for (int i=0; i<num; i++)
  22.156 +            fprintf(stderr, "%3d ",map[i]);
  22.157 +        fprintf(stderr, "\n");
  22.158 +    }
  22.159 +    
  22.160 +    for (int i=0; i<num; i++)
  22.161 +        Remove(hash,map[i]);
  22.162 +}
  22.163 +
  22.164 +TEST(Hash,Words) {
  22.165 +    const int kIterations = 3;
  22.166 +    readWords();
  22.167 +    double totalAdd = 0.0, totalGet = 0.0, totalRemove = 0.0;
  22.168 +    for (int iter=0; iter<kIterations; iter++) {
  22.169 +        printf("Adding words to Hash...\n");
  22.170 +        Hash hash;
  22.171 +        {
  22.172 +            Timer t("adding");
  22.173 +            for( int i=0; i<sNWords; i++)
  22.174 +                hash.put(sWords[i], sWords[i]);
  22.175 +            EXPECT_EQ(sNWords, hash.count());
  22.176 +            totalAdd += t.elapsed();
  22.177 +        }        
  22.178 +        {
  22.179 +            Timer t("getting");
  22.180 +            for( int i=0; i<sNWords; i++)
  22.181 +                EXPECT_EQ( sWords[i] ,  hash.get(sWords[i]) );
  22.182 +            totalGet += t.elapsed();
  22.183 +        }        
  22.184 +        {
  22.185 +            Timer t("removing");
  22.186 +            for( int i=0; i<sNWords; i++)
  22.187 +                EXPECT_TRUE( hash.remove(sWords[i]) );
  22.188 +            totalRemove += t.elapsed();
  22.189 +        }        
  22.190 +    }
  22.191 +    printf("Average put   = %.0lfns\n", totalAdd/kIterations/sNWords*1e9);
  22.192 +    printf("Average get   = %.0lfns\n", totalGet/kIterations/sNWords*1e9);
  22.193 +    printf("Average remove= %.0lfns\n", totalRemove/kIterations/sNWords*1e9);
  22.194 +}
  22.195 +
  22.196 +
    23.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    23.2 +++ b/test/Ottoman_test.cpp	Sun Sep 20 15:14:12 2009 -0700
    23.3 @@ -0,0 +1,203 @@
    23.4 +/*
    23.5 + *  Ottoman_test.cpp
    23.6 + *  Ottoman
    23.7 + *
    23.8 + *  Created by Jens Alfke on 9/11/09.
    23.9 + *  Copyright 2009 Jens Alfke. All rights reserved.
   23.10 + *  BSD-Licensed: See the file "LICENSE.txt" for details.
   23.11 + */
   23.12 +
   23.13 +#include <gtest/gtest.h>
   23.14 +#include "TestUtils.h"
   23.15 +#include "Chunk.h"
   23.16 +#include "Ottoman.h"
   23.17 +#include "VersionDictionary.h"
   23.18 +#include <fcntl.h>
   23.19 +#include <stdio.h>
   23.20 +
   23.21 +using namespace Mooseyard;
   23.22 +
   23.23 +
   23.24 +TEST(Ottoman,Build) {
   23.25 +    ASSERT_EQ(8, sizeof(Chunk));
   23.26 +    ASSERT_EQ(8, sizeof(KeyValueChunk));
   23.27 +        
   23.28 +    time_t startTime = ::time(NULL);
   23.29 +    time_t createTime;
   23.30 +    {
   23.31 +        Ottoman otto;
   23.32 +        EXPECT_EQ((void*)NULL, otto.lastVersion());
   23.33 +        ASSERT_NE((void*)NULL, otto.currentVersion());
   23.34 +        readWords();
   23.35 +        for( int i=0; i<sNWords; i++) {
   23.36 +            Blob kv(sWords[i]);
   23.37 +            otto.currentVersion()->put(Key(kv),kv);
   23.38 +        }
   23.39 +        Timer t("Saving Ottoman", sNWords);
   23.40 +        otto.saveAs("/tmp/test.ottoman");
   23.41 +        
   23.42 +        VersionDictionary *last = otto.lastVersion();
   23.43 +        ASSERT_NE((void*)NULL, last);
   23.44 +        EXPECT_EQ(sNWords, last->count());
   23.45 +        ASSERT_NE((void*)NULL, otto.currentVersion());
   23.46 +        EXPECT_FALSE(otto.currentVersion()->isChanged());
   23.47 +        createTime = last->timestamp();
   23.48 +        EXPECT_GE(createTime, startTime);
   23.49 +        EXPECT_LE(createTime, ::time(NULL));
   23.50 +    }
   23.51 +    {
   23.52 +        Ottoman otto("/tmp/test.ottoman");
   23.53 +        VersionDictionary *last = otto.lastVersion();
   23.54 +        ASSERT_NE((void*)NULL, last);
   23.55 +        ASSERT_EQ(0, last->generation());
   23.56 +        ASSERT_EQ(createTime, last->timestamp());
   23.57 +        {
   23.58 +            Timer t("Reading from Ottoman", sNWords);
   23.59 +            EXPECT_EQ( sNWords ,  last->count() );
   23.60 +            for( int i=0; i<sNWords; i++) {
   23.61 +                Key key(sWords[i]);
   23.62 +                Blob value = last->get(key);
   23.63 +                ASSERT_TRUE(value);
   23.64 +                ASSERT_TRUE( value.equals(key) ) << "expected '" << key << "', got '" << value << "' (i=" << i <<")";
   23.65 +            }
   23.66 +        }
   23.67 +        
   23.68 +        printf("Iterating through the Ottoman...\n");
   23.69 +        Timer t("Iterating Ottoman", sNWords);
   23.70 +        int n=0;
   23.71 +        for( VersionDictionary::Iterator it(last); it; ++it) {
   23.72 +            n++;
   23.73 +            ASSERT_TRUE(it.key().length > 0 && it.key().length < 50);
   23.74 +            ASSERT_TRUE(it.key().equals(it.value()));
   23.75 +            ASSERT_EQ( 0, ((size_t)it.value().bytes & 0x3) );  // 4-byte aligned
   23.76 +        }
   23.77 +        ASSERT_EQ(sNWords, n);
   23.78 +    }
   23.79 +    {
   23.80 +        printf("Opening Ottoman...\n");
   23.81 +        Ottoman otto("/tmp/test.ottoman");
   23.82 +        OverlayDictionary *current = otto.currentVersion();
   23.83 +        ASSERT_TRUE(current != NULL);
   23.84 +        EXPECT_EQ( sNWords ,  current->count() );
   23.85 +        EXPECT_TRUE(current->get("asparagus").equals("asparagus"));
   23.86 +        
   23.87 +        current->put("animal", "AMINAL");
   23.88 +        current->put("growf", "growf");
   23.89 +        EXPECT_TRUE(current->remove("asparagus"));
   23.90 +        
   23.91 +        EXPECT_EQ( sNWords, current->count() );
   23.92 +        EXPECT_TRUE(current->get("animal").equals("AMINAL"));
   23.93 +        EXPECT_TRUE(current->get("growf").equals("growf"));
   23.94 +        EXPECT_EQ( NULL, current->get("asparagus").bytes );
   23.95 +        EXPECT_TRUE(!current->contains("asparagus"));
   23.96 +        
   23.97 +        int n=0;
   23.98 +        for( OverlayDictionary::Iterator it(*current); it; ++it) {
   23.99 +            n++;
  23.100 +            ASSERT_TRUE(!it.key().equals("asparagus"));
  23.101 +        }
  23.102 +        ASSERT_EQ(sNWords, n);
  23.103 +        
  23.104 +        printf("Saving Ottoman...\n");
  23.105 +        {
  23.106 +            Timer t("Saving Ottoman");
  23.107 +            otto.save();
  23.108 +        }
  23.109 +        
  23.110 +        EXPECT_EQ( sNWords, current->count() );
  23.111 +        EXPECT_TRUE(current->get("animal").equals("AMINAL"));
  23.112 +        EXPECT_TRUE(current->get("growf").equals("growf"));
  23.113 +        EXPECT_EQ( NULL, current->get("asparagus").bytes );
  23.114 +        EXPECT_TRUE(!current->contains("asparagus"));
  23.115 +        
  23.116 +        n=0;
  23.117 +        for( OverlayDictionary::Iterator it(*current); it; ++it) {
  23.118 +            n++;
  23.119 +            ASSERT_TRUE(!it.key().equals("asparagus"));
  23.120 +        }
  23.121 +        ASSERT_EQ(sNWords, n);
  23.122 +        
  23.123 +        EXPECT_EQ(1, otto.lastVersion()->generation());
  23.124 +        const VersionDictionary *prev = otto.lastVersion()->previousVersion();
  23.125 +        ASSERT_TRUE(prev != NULL);
  23.126 +        EXPECT_EQ(0, prev->generation());
  23.127 +        EXPECT_TRUE(prev->get("asparagus").equals("asparagus"));
  23.128 +        EXPECT_TRUE(prev->get("growf").equals(NULL));
  23.129 +    }
  23.130 +    {
  23.131 +        printf("Re-opening Ottoman...\n");
  23.132 +        Ottoman otto("/tmp/test.ottoman", false);
  23.133 +        ASSERT_EQ(NULL, otto.currentVersion());
  23.134 +        VersionDictionary *last = otto.lastVersion();
  23.135 +        ASSERT_TRUE(last != NULL);
  23.136 +        EXPECT_EQ(1, last->generation());
  23.137 +        EXPECT_GE(last->timestamp(), createTime);
  23.138 +        EXPECT_LE(last->timestamp(), ::time(NULL));
  23.139 +        EXPECT_EQ( sNWords ,  last->count() );
  23.140 +        EXPECT_TRUE(last->get("animal").equals("AMINAL"));
  23.141 +        EXPECT_TRUE(last->get("growf").equals("growf"));
  23.142 +        EXPECT_EQ( NULL, last->get("asparagus").bytes );
  23.143 +        EXPECT_TRUE(!last->contains("asparagus"));
  23.144 +        
  23.145 +        int n=0;
  23.146 +        for( VersionDictionary::Iterator it(last); it; ++it) {
  23.147 +            n++;
  23.148 +            ASSERT_TRUE(!it.key().equals("asparagus"));
  23.149 +        }
  23.150 +        ASSERT_EQ(sNWords, n);
  23.151 +    }
  23.152 +    {
  23.153 +        printf("Writing Ottoman to a new file...\n");
  23.154 +        Ottoman oldhf("/tmp/test.ottoman");
  23.155 +        
  23.156 +        oldhf.saveAs("/tmp/test2.ottoman");
  23.157 +    }
  23.158 +    {
  23.159 +        printf("Opening new file...\n");
  23.160 +        Ottoman otto("/tmp/test2.ottoman");
  23.161 +        OverlayDictionary *current = otto.currentVersion();
  23.162 +        ASSERT_TRUE(current != NULL);
  23.163 +        
  23.164 +        EXPECT_EQ( sNWords ,  current->count() );
  23.165 +        EXPECT_TRUE(current->get("animal").equals("AMINAL"));
  23.166 +        EXPECT_TRUE(current->get("growf").equals("growf"));
  23.167 +        EXPECT_EQ( NULL, current->get("asparagus").bytes );
  23.168 +        EXPECT_TRUE(!current->contains("asparagus"));
  23.169 +        
  23.170 +        int n=0;
  23.171 +        for( OverlayDictionary::Iterator it(*current); it; ++it) {
  23.172 +            n++;
  23.173 +            ASSERT_TRUE(!it.key().equals("asparagus"));
  23.174 +        }
  23.175 +        ASSERT_EQ(sNWords, n);
  23.176 +
  23.177 +        printf("Iterating the chunks...\n");
  23.178 +        int lastType = -1;
  23.179 +        int count = 0;
  23.180 +        n = 0;
  23.181 +        ChunkIterator *it = otto.chunkIterator();
  23.182 +        for (; *it; it->next()) {
  23.183 +            uint16_t type = it->chunk()->type();
  23.184 +            if (type != lastType) {
  23.185 +                if (count > 0)
  23.186 +                    printf("%6d\n", count);
  23.187 +                printf("type %u ... ", type);
  23.188 +                lastType = type;
  23.189 +                count = 0;
  23.190 +            }
  23.191 +            count++;
  23.192 +            ASSERT_LE(type, 3);
  23.193 +            if (type != 0)      // don't count padding chunks
  23.194 +                n++;
  23.195 +        }
  23.196 +        if (count > 0)
  23.197 +            printf("%6d\n", count);
  23.198 +        printf("Iterated over %i chunks\n",n);
  23.199 +        EXPECT_EQ(sNWords+256+1, n);
  23.200 +        EXPECT_TRUE(it->atEOF());
  23.201 +        
  23.202 +        printf("Scavenging...\n");
  23.203 +        EXPECT_TRUE( otto.scavenge() );
  23.204 +    }
  23.205 +}    
  23.206 +
    24.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    24.2 +++ b/test/TestUtils.cpp	Sun Sep 20 15:14:12 2009 -0700
    24.3 @@ -0,0 +1,104 @@
    24.4 +/*
    24.5 + *  TestUtils.cpp
    24.6 + *  Ottoman
    24.7 + *
    24.8 + *  Created by Jens Alfke on 9/12/09.
    24.9 + *  Copyright 2009 Jens Alfke. All rights reserved.
   24.10 + *  BSD-Licensed: See the file "LICENSE.txt" for details.
   24.11 + */
   24.12 +
   24.13 +#include "TestUtils.h"
   24.14 +#include "File.h"
   24.15 +#include <time.h>
   24.16 +#include <sys/time.h>
   24.17 +
   24.18 +namespace Mooseyard {
   24.19 +
   24.20 +    std::ostream& operator<< (std::ostream &out, const Blob &blob) {
   24.21 +        char str[blob.length+1];
   24.22 +        memcpy(str,blob.bytes,blob.length);
   24.23 +        str[blob.length] = 0;
   24.24 +        out << str;
   24.25 +        return out;
   24.26 +    }
   24.27 +    
   24.28 +    void shuffle(int a[], int n, unsigned seed) {
   24.29 +        if (seed==0) {
   24.30 +            srandomdev();
   24.31 +            seed = random();
   24.32 +        }
   24.33 +        fprintf(stderr,"shuffle(n=%i, seed=%u)\n", n,seed);
   24.34 +        srandom(seed);
   24.35 +        for (int i=0; i<n-1; i++) {
   24.36 +            int j = i + (random() % (n-i));
   24.37 +            int temp = a[i];
   24.38 +            a[i] = a[j];
   24.39 +            a[j] = temp;
   24.40 +        }
   24.41 +    }
   24.42 +    
   24.43 +    char **sWords;
   24.44 +    int sNWords;
   24.45 +    
   24.46 +    void readWords() {
   24.47 +        if (!sWords) {
   24.48 +            sWords = new char*[250000];
   24.49 +            sNWords = 0;
   24.50 +            
   24.51 +            FILE *in = fopen("/usr/share/dict/words", "r");
   24.52 +            const char *word;
   24.53 +            size_t wordLen;
   24.54 +            while (NULL != (word=fgetln(in,&wordLen))) {
   24.55 +                if (word[wordLen-1]=='\n')
   24.56 +                    wordLen--;
   24.57 +                char *str = new char[wordLen+1];
   24.58 +                memcpy(str, word, wordLen);
   24.59 +                str[wordLen] = 0;
   24.60 +                sWords[sNWords] = str;
   24.61 +                //if( sNWords % 10000 == 0)
   24.62 +                //    printf("'%s' ... ", sWords[sNWords]->string());
   24.63 +                sNWords++;
   24.64 +            }
   24.65 +        }
   24.66 +    }
   24.67 +    
   24.68 +#pragma mark -
   24.69 +#pragma mark TIMER:
   24.70 +    
   24.71 +    Timer::Timer (const char *operation, int divisor) {
   24.72 +        _operation = operation;
   24.73 +        _divisor = divisor;
   24.74 +        _cpuTime = clock();
   24.75 +        _time = now();
   24.76 +    }
   24.77 +    
   24.78 +    Timer::~Timer() {
   24.79 +        double elapsedCPU = (clock() - _cpuTime) / 1.0e6;
   24.80 +        double elapsed = now() - _time;
   24.81 +        printf("### %s took %.6lf sec (used %.6lf sec CPU)", _operation, elapsed, elapsedCPU);
   24.82 +        if (_divisor > 1)
   24.83 +            printf(" ... per item: %.3lf usec, %.3lf usec CPU", elapsed/_divisor*1e6, elapsedCPU/_divisor*1e6);
   24.84 +        printf("\n");
   24.85 +    }
   24.86 +    
   24.87 +    double Timer::now() {
   24.88 +        struct timeval t;
   24.89 +        gettimeofday(&t,NULL);
   24.90 +        return (double)t.tv_sec + t.tv_usec/1.0e6;
   24.91 +    }
   24.92 +    
   24.93 +}
   24.94 +
   24.95 +
   24.96 +using namespace Mooseyard;
   24.97 +
   24.98 +int main(int argc, char **argv) {
   24.99 +    srandomdev();
  24.100 +    try {
  24.101 +        ::testing::InitGoogleTest(&argc, argv);
  24.102 +        return RUN_ALL_TESTS();
  24.103 +    } catch (const File::Error &err) {
  24.104 +        fprintf(stderr, "\n*** File::Error thrown: %i/%s\n", err.code,err.message);
  24.105 +        throw;
  24.106 +    }
  24.107 +}
    25.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    25.2 +++ b/test/TestUtils.h	Sun Sep 20 15:14:12 2009 -0700
    25.3 @@ -0,0 +1,37 @@
    25.4 +/*
    25.5 + *  TestUtils.h
    25.6 + *  Ottoman
    25.7 + *
    25.8 + *  Created by Jens Alfke on 9/2/09.
    25.9 + *  Copyright 2009 Jens Alfke. All rights reserved.
   25.10 + *  BSD-Licensed: See the file "LICENSE.txt" for details.
   25.11 + */
   25.12 +
   25.13 +#include <gtest/gtest.h>    // Get gtest from <http://code.google.com/p/googletest/>
   25.14 +#include <iosfwd>
   25.15 +#include "Base.h"
   25.16 +
   25.17 +namespace Mooseyard {
   25.18 +
   25.19 +    std::ostream& operator<< (std::ostream &out, const Blob&);
   25.20 +
   25.21 +    void shuffle(int a[], int n, unsigned seed =0);
   25.22 +    
   25.23 +    extern char **sWords;
   25.24 +    extern int sNWords;
   25.25 +    
   25.26 +    void readWords();
   25.27 +        
   25.28 +    class Timer {
   25.29 +    public:
   25.30 +        Timer (const char *operation, int divisor =1);
   25.31 +        ~Timer();
   25.32 +        double elapsed()                                    {return now() - _time;}
   25.33 +        static double now();
   25.34 +    private:
   25.35 +        const char *_operation;
   25.36 +        int _divisor;
   25.37 +        double _cpuTime, _time;
   25.38 +    };
   25.39 +    
   25.40 +}
    26.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    26.2 +++ b/test/VersionDictionary_test.cpp	Sun Sep 20 15:14:12 2009 -0700
    26.3 @@ -0,0 +1,265 @@
    26.4 +/*
    26.5 + *  VersionDictionary_test.cpp
    26.6 + *  Ottoman
    26.7 + *
    26.8 + *  Created by Jens Alfke on 9/2/09.
    26.9 + *  Copyright 2009 Jens Alfke. All rights reserved.
   26.10 + *  BSD-Licensed: See the file "LICENSE.txt" for details.
   26.11 + */
   26.12 +
   26.13 +
   26.14 +#define VERSIONDICTIONARY_TESTING 1
   26.15 +
   26.16 +#include <gtest/gtest.h>
   26.17 +#include "TestUtils.h"
   26.18 +#include "Chunk.h"
   26.19 +#include "File.h"
   26.20 +#include "VersionDictionary.h"
   26.21 +#include "Hash.h"
   26.22 +#include <fcntl.h>
   26.23 +#include <stdio.h>
   26.24 +
   26.25 +using namespace Mooseyard;
   26.26 +
   26.27 +
   26.28 +class OverlayVersionDictionary :public OverlayDictionary {
   26.29 +public:
   26.30 +    OverlayVersionDictionary (File *file)
   26.31 +    :OverlayDictionary(new VersionDictionary(file))
   26.32 +    { }  
   26.33 +    
   26.34 +    int generation() const                  {return ((VersionDictionary*)base())->generation();}
   26.35 +    time_t timestamp() const                {return ((VersionDictionary*)base())->timestamp();}
   26.36 +
   26.37 +    void save(){
   26.38 +        saveAs(((VersionDictionary*)base())->file());
   26.39 +    }
   26.40 +    
   26.41 +    void saveAs (File *dstFile) {
   26.42 +        VersionDictionary* oldBase = (VersionDictionary*) base();
   26.43 +        revertTo( ((VersionDictionary*)base())->_appendAndOpen(overlay(), dstFile, baseReplaced()) );
   26.44 +        delete oldBase;
   26.45 +    }
   26.46 +};
   26.47 +
   26.48 +
   26.49 +
   26.50 +TEST(File,HasPath) {
   26.51 +    {
   26.52 +        File f("/tmp/jubba", O_RDWR | O_CREAT | O_TRUNC);
   26.53 +        f.write("howdy");
   26.54 +    }
   26.55 +    {
   26.56 +        File f("/tmp/jubba", O_RDWR);
   26.57 +        EXPECT_TRUE(f.hasPath("/tmp/jubba"));
   26.58 +        File::unlink("/tmp/jubba");
   26.59 +        EXPECT_FALSE(f.hasPath("/tmp/jubba"));
   26.60 +        
   26.61 +        File f2("/tmp/jubba", O_RDWR | O_CREAT | O_TRUNC);
   26.62 +        f2.write("howdy");
   26.63 +        f2.flush();
   26.64 +
   26.65 +        EXPECT_FALSE(f.hasPath("/tmp/jubba"));
   26.66 +        EXPECT_TRUE(f2.hasPath("/tmp/jubba"));
   26.67 +    }
   26.68 +    File::unlink("/tmp/jubba");
   26.69 +}
   26.70 +
   26.71 +
   26.72 +static int TestIterate (File *file) {
   26.73 +    printf("Iterating the chunks...\n");
   26.74 +    int lastType = -1;
   26.75 +    int count = 0;
   26.76 +    int n = 0;
   26.77 +    ChunkIterator it(file, 4);
   26.78 +    for (; it; ++it) {
   26.79 +        uint16_t type = it.chunk()->type();
   26.80 +        if (type != lastType) {
   26.81 +            if (count > 0)
   26.82 +                printf("%6d\n", count);
   26.83 +            printf("type %u ... ", type);
   26.84 +            lastType = type;
   26.85 +            count = 0;
   26.86 +        }
   26.87 +        count++;
   26.88 +        EXPECT_LE(type, 3);
   26.89 +        if (type != 0)      // don't count padding chunks
   26.90 +            n++;
   26.91 +    }
   26.92 +    if (count > 0)
   26.93 +        printf("%6d\n", count);
   26.94 +    EXPECT_TRUE(it.atEOF());
   26.95 +    return n;
   26.96 +}    
   26.97 +
   26.98 +
   26.99 +static void TestWithWords (const int nWords) {
  26.100 +    ASSERT_EQ(8, sizeof(Chunk));
  26.101 +    ASSERT_EQ(8, sizeof(KeyValueChunk));
  26.102 +    
  26.103 +    printf("Building dictionary of %i words...\n", nWords);
  26.104 +    readWords();
  26.105 +    HashDictionary dict;
  26.106 +    for( int i=0; i<nWords; i++) {
  26.107 +        Blob kv(sWords[i]);
  26.108 +        dict.put(Key(kv),kv);
  26.109 +    }
  26.110 +    
  26.111 +    time_t startTime = ::time(NULL);
  26.112 +    time_t createTime;
  26.113 +    {
  26.114 +        File file("/tmp/hashfiletest", O_RDWR | O_CREAT | O_TRUNC);
  26.115 +        VersionDictionary *hf;
  26.116 +        {
  26.117 +            Timer t("Creating & writing VersionDictionary", nWords);
  26.118 +            file.write("Ha5h", 4);     // VersionDictionary won't write to an empty file
  26.119 +            hf = VersionDictionary::create(&file, &dict);
  26.120 +        }
  26.121 +        printf("File size: %llu bytes\n", file.length());
  26.122 +        ASSERT_TRUE(hf!=NULL);
  26.123 +        ASSERT_EQ(0, hf->generation());
  26.124 +        createTime = hf->timestamp();
  26.125 +        ASSERT_GE(createTime, startTime);
  26.126 +        ASSERT_LE(createTime, ::time(NULL));
  26.127 +        delete hf;
  26.128 +    }
  26.129 +    {
  26.130 +        File file("/tmp/hashfiletest");
  26.131 +        VersionDictionary hf(&file);
  26.132 +        ASSERT_EQ(0, hf.generation());
  26.133 +        ASSERT_EQ(createTime, hf.timestamp());
  26.134 +        {
  26.135 +            Timer t("Reading from VersionDictionary", nWords);
  26.136 +            EXPECT_EQ( nWords ,  hf.count() );
  26.137 +            for( int i=0; i<nWords; i++) {
  26.138 +                Key key(sWords[i]);
  26.139 +                Blob value = hf.get(key);
  26.140 +                ASSERT_TRUE(value);
  26.141 +                ASSERT_TRUE( value.equals(key) ) << "expected '" << key << "', got '" << value << "' (i=" << i <<")";
  26.142 +            }
  26.143 +        }
  26.144 +        
  26.145 +        printf("Iterating through the VersionDictionary...\n");
  26.146 +        Timer t("Iterating VersionDictionary", nWords);
  26.147 +        int n=0;
  26.148 +        for( VersionDictionary::Iterator it(&hf); it; ++it) {
  26.149 +            n++;
  26.150 +            ASSERT_TRUE(it.key().length > 0 && it.key().length < 50);
  26.151 +            ASSERT_TRUE(it.key().equals(it.value()));
  26.152 +            ASSERT_EQ( 0, ((size_t)it.value().bytes & 0x3) );  // 4-byte aligned
  26.153 +        }
  26.154 +        ASSERT_EQ(nWords, n);
  26.155 +    }
  26.156 +    {
  26.157 +        printf("Opening OverlayVersionDictionary...\n");
  26.158 +        File file("/tmp/hashfiletest", O_RDWR);
  26.159 +        OverlayVersionDictionary hf(&file);
  26.160 +        EXPECT_EQ( nWords ,  hf.count() );
  26.161 +        EXPECT_TRUE(hf.get("abatement").equals("abatement"));
  26.162 +        
  26.163 +        hf.put("abaser", "DEBASER");
  26.164 +        hf.put("growf", "growf");
  26.165 +        EXPECT_TRUE(hf.remove("abatement"));
  26.166 +        
  26.167 +        EXPECT_EQ( nWords, hf.count() );
  26.168 +        EXPECT_TRUE(hf.get("abaser").equals("DEBASER"));
  26.169 +        EXPECT_TRUE(hf.get("growf").equals("growf"));
  26.170 +        EXPECT_EQ( NULL, hf.get("abatement").bytes );
  26.171 +        EXPECT_TRUE(!hf.contains("abatement"));
  26.172 +        
  26.173 +        int n=0;
  26.174 +        for( OverlayVersionDictionary::Iterator it(hf); it; ++it) {
  26.175 +            n++;
  26.176 +            ASSERT_TRUE(!it.key().equals("abatement"));
  26.177 +        }
  26.178 +        ASSERT_EQ(nWords, n);
  26.179 +        
  26.180 +        printf("Saving OverlayVersionDictionary...\n");
  26.181 +        {
  26.182 +            Timer t("Saving OverlayVersionDictionary");
  26.183 +            hf.save();
  26.184 +        }
  26.185 +        printf("File size: %llu bytes\n", file.length());
  26.186 +        
  26.187 +        EXPECT_EQ( nWords, hf.count() );
  26.188 +        EXPECT_TRUE(hf.get("abaser").equals("DEBASER"));
  26.189 +        EXPECT_TRUE(hf.get("growf").equals("growf"));
  26.190 +        EXPECT_EQ( NULL, hf.get("abatement").bytes );
  26.191 +        EXPECT_TRUE(!hf.contains("abatement"));
  26.192 +        
  26.193 +        n=0;
  26.194 +        for( OverlayVersionDictionary::Iterator it(hf); it; ++it) {
  26.195 +            n++;
  26.196 +            ASSERT_TRUE(!it.key().equals("abatement"));
  26.197 +        }
  26.198 +        ASSERT_EQ(nWords, n);
  26.199 +    }
  26.200 +    {
  26.201 +        printf("Re-opening OverlayVersionDictionary...\n");
  26.202 +        File file("/tmp/hashfiletest");
  26.203 +        OverlayVersionDictionary hf(&file);
  26.204 +        
  26.205 +        ASSERT_EQ(1, hf.generation());
  26.206 +        ASSERT_GE(hf.timestamp(), createTime);
  26.207 +        ASSERT_LE(hf.timestamp(), ::time(NULL));
  26.208 +        EXPECT_EQ( nWords ,  hf.count() );
  26.209 +        EXPECT_TRUE(hf.get("abaser").equals("DEBASER"));
  26.210 +        EXPECT_TRUE(hf.get("growf").equals("growf"));
  26.211 +        EXPECT_EQ( NULL, hf.get("abatement").bytes );
  26.212 +        EXPECT_TRUE(!hf.contains("abatement"));
  26.213 +        
  26.214 +        int n=0;
  26.215 +        for( OverlayVersionDictionary::Iterator it(hf); it; ++it) {
  26.216 +            n++;
  26.217 +            ASSERT_TRUE(!it.key().equals("abatement"));
  26.218 +        }
  26.219 +        ASSERT_EQ(nWords, n);
  26.220 +
  26.221 +        n = TestIterate(&file);
  26.222 +        EXPECT_GE(n, nWords+1+4);
  26.223 +        if (nWords > 1000)
  26.224 +            EXPECT_GE(n, nWords+256+4);
  26.225 +    }
  26.226 +    {
  26.227 +        printf("Writing VersionDictionary to a new file...\n");
  26.228 +        File oldFile("/tmp/hashfiletest");
  26.229 +        OverlayVersionDictionary oldhf(&oldFile);
  26.230 +        
  26.231 +        File newFile("/tmp/hashfiletest2", O_RDWR | O_CREAT | O_TRUNC);
  26.232 +        newFile.write("Ha5h", 4);     // VersionDictionary won't write to an empty file
  26.233 +        oldhf.saveAs(&newFile);
  26.234 +        printf("File size: %llu bytes\n", newFile.length());
  26.235 +    }
  26.236 +    {
  26.237 +        printf("Opening new file...\n");
  26.238 +        File file("/tmp/hashfiletest2");
  26.239 +        OverlayVersionDictionary hf(&file);
  26.240 +        
  26.241 +        EXPECT_EQ( nWords ,  hf.count() );
  26.242 +        EXPECT_TRUE(hf.get("abaser").equals("DEBASER"));
  26.243 +        EXPECT_TRUE(hf.get("growf").equals("growf"));
  26.244 +        EXPECT_EQ( NULL, hf.get("abatement").bytes );
  26.245 +        EXPECT_TRUE(!hf.contains("abatement"));
  26.246 +        
  26.247 +        int n=0;
  26.248 +        for( OverlayVersionDictionary::Iterator it(hf); it; ++it) {
  26.249 +            n++;
  26.250 +            ASSERT_TRUE(!it.key().equals("abatement"));
  26.251 +        }
  26.252 +        ASSERT_EQ(nWords, n);
  26.253 +        
  26.254 +        n = TestIterate(&file);
  26.255 +        EXPECT_GE(n, nWords+1+1);
  26.256 +        if (nWords > 1000)
  26.257 +            EXPECT_EQ(nWords+256+1, n);
  26.258 +    }
  26.259 +}    
  26.260 +
  26.261 +
  26.262 +TEST(VersionDictionary,BuildSmall) {
  26.263 +    TestWithWords(100);
  26.264 +}
  26.265 +
  26.266 +TEST(VersionDictionary,BuildLarge) {
  26.267 +    TestWithWords(sNWords);
  26.268 +}