Initial Cocoa (Objective-C) API.
1.1 --- a/Ottoman.xcodeproj/project.pbxproj Thu Sep 24 21:46:17 2009 -0700
1.2 +++ b/Ottoman.xcodeproj/project.pbxproj Thu Sep 24 21:47:06 2009 -0700
1.3 @@ -8,6 +8,11 @@
1.4
1.5 /* Begin PBXBuildFile section */
1.6 27156CAA104C9C44009EBD39 /* gtest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27156CA9104C9C44009EBD39 /* gtest.framework */; };
1.7 + 2754DD87106BCBCF00365FAA /* MYOttoman_test.m in Sources */ = {isa = PBXBuildFile; fileRef = 2754DD86106BCBCF00365FAA /* MYOttoman_test.m */; };
1.8 + 2754DDC5106BD20A00365FAA /* Test.m in Sources */ = {isa = PBXBuildFile; fileRef = 2754DDC4106BD20A00365FAA /* Test.m */; };
1.9 + 2754DDEE106BD3BD00365FAA /* Logging.m in Sources */ = {isa = PBXBuildFile; fileRef = 2754DDED106BD3BD00365FAA /* Logging.m */; };
1.10 + 2754DDF3106BD3D300365FAA /* ExceptionUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 2754DDF2106BD3D300365FAA /* ExceptionUtils.m */; };
1.11 + 2754DE2D106BD65600365FAA /* CollectionUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 2754DE2C106BD65600365FAA /* CollectionUtils.m */; };
1.12 27603901105AC81200D931A7 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27603900105AC81200D931A7 /* CoreFoundation.framework */; };
1.13 276E5BCD1066D13D008A2171 /* Base.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 276E5BC41066D13D008A2171 /* Base.cpp */; };
1.14 276E5BCE1066D13D008A2171 /* Chunk.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 276E5BC51066D13D008A2171 /* Chunk.cpp */; };
1.15 @@ -41,8 +46,21 @@
1.16 276E5CC4106731C7008A2171 /* File.h in Headers */ = {isa = PBXBuildFile; fileRef = 276E5BBD1066D135008A2171 /* File.h */; };
1.17 276E5CC5106731C8008A2171 /* Ottoman.h in Headers */ = {isa = PBXBuildFile; fileRef = 276E5BC11066D135008A2171 /* Ottoman.h */; };
1.18 276E5CC6106731C8008A2171 /* VersionDictionary.h in Headers */ = {isa = PBXBuildFile; fileRef = 276E5BC21066D135008A2171 /* VersionDictionary.h */; };
1.19 + 276E5D141067D2AD008A2171 /* MYOttoman.mm in Sources */ = {isa = PBXBuildFile; fileRef = 276E5D0A1067D24A008A2171 /* MYOttoman.mm */; };
1.20 + 276E5D151067D2B2008A2171 /* libOttoman.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 276E5CAE1067315D008A2171 /* libOttoman.a */; };
1.21 + 276E5D2E1067F86E008A2171 /* MYVersionDictionary.mm in Sources */ = {isa = PBXBuildFile; fileRef = 276E5D2D1067F86E008A2171 /* MYVersionDictionary.mm */; };
1.22 /* End PBXBuildFile section */
1.23
1.24 +/* Begin PBXContainerItemProxy section */
1.25 + 276E5D7810692FA7008A2171 /* PBXContainerItemProxy */ = {
1.26 + isa = PBXContainerItemProxy;
1.27 + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */;
1.28 + proxyType = 1;
1.29 + remoteGlobalIDString = 276E5CAD1067315D008A2171;
1.30 + remoteInfo = "Static Library";
1.31 + };
1.32 +/* End PBXContainerItemProxy section */
1.33 +
1.34 /* Begin PBXCopyFilesBuildPhase section */
1.35 8DD76F690486A84900D96B5E /* CopyFiles */ = {
1.36 isa = PBXCopyFilesBuildPhase;
1.37 @@ -57,6 +75,15 @@
1.38
1.39 /* Begin PBXFileReference section */
1.40 27156CA9104C9C44009EBD39 /* gtest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = gtest.framework; path = /Library/Frameworks/gtest.framework; sourceTree = "<absolute>"; };
1.41 + 2754DD86106BCBCF00365FAA /* MYOttoman_test.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYOttoman_test.m; sourceTree = "<group>"; };
1.42 + 2754DDC3106BD20A00365FAA /* Test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Test.h; sourceTree = "<group>"; };
1.43 + 2754DDC4106BD20A00365FAA /* Test.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Test.m; sourceTree = "<group>"; };
1.44 + 2754DDEC106BD3BD00365FAA /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Logging.h; sourceTree = "<group>"; };
1.45 + 2754DDED106BD3BD00365FAA /* Logging.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Logging.m; sourceTree = "<group>"; };
1.46 + 2754DDF1106BD3D300365FAA /* ExceptionUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ExceptionUtils.h; sourceTree = "<group>"; };
1.47 + 2754DDF2106BD3D300365FAA /* ExceptionUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ExceptionUtils.m; sourceTree = "<group>"; };
1.48 + 2754DE2B106BD65600365FAA /* CollectionUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CollectionUtils.h; sourceTree = "<group>"; };
1.49 + 2754DE2C106BD65600365FAA /* CollectionUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CollectionUtils.m; sourceTree = "<group>"; };
1.50 27603900105AC81200D931A7 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; };
1.51 276E5BBA1066D135008A2171 /* Base.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Base.h; sourceTree = "<group>"; };
1.52 276E5BBB1066D135008A2171 /* Chunk.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Chunk.h; sourceTree = "<group>"; };
1.53 @@ -83,6 +110,12 @@
1.54 276E5BDB1066D142008A2171 /* TestUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TestUtils.h; sourceTree = "<group>"; };
1.55 276E5BDC1066D142008A2171 /* VersionDictionary_test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VersionDictionary_test.cpp; sourceTree = "<group>"; };
1.56 276E5CAE1067315D008A2171 /* libOttoman.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libOttoman.a; sourceTree = BUILT_PRODUCTS_DIR; };
1.57 + 276E5D091067D24A008A2171 /* MYOttoman.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYOttoman.h; sourceTree = "<group>"; };
1.58 + 276E5D0A1067D24A008A2171 /* MYOttoman.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MYOttoman.mm; sourceTree = "<group>"; };
1.59 + 276E5D101067D27E008A2171 /* OttomanCocoa */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = OttomanCocoa; sourceTree = BUILT_PRODUCTS_DIR; };
1.60 + 276E5D2C1067F86E008A2171 /* MYVersionDictionary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYVersionDictionary.h; sourceTree = "<group>"; };
1.61 + 276E5D2D1067F86E008A2171 /* MYVersionDictionary.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MYVersionDictionary.mm; sourceTree = "<group>"; };
1.62 + 276E5D451069246E008A2171 /* MYOttoman_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYOttoman_internal.h; sourceTree = "<group>"; };
1.63 8DD76F6C0486A84900D96B5E /* OttomanTest */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = OttomanTest; sourceTree = BUILT_PRODUCTS_DIR; };
1.64 /* End PBXFileReference section */
1.65
1.66 @@ -94,6 +127,14 @@
1.67 );
1.68 runOnlyForDeploymentPostprocessing = 0;
1.69 };
1.70 + 276E5D0E1067D27E008A2171 /* Frameworks */ = {
1.71 + isa = PBXFrameworksBuildPhase;
1.72 + buildActionMask = 2147483647;
1.73 + files = (
1.74 + 276E5D151067D2B2008A2171 /* libOttoman.a in Frameworks */,
1.75 + );
1.76 + runOnlyForDeploymentPostprocessing = 0;
1.77 + };
1.78 8DD76F660486A84900D96B5E /* Frameworks */ = {
1.79 isa = PBXFrameworksBuildPhase;
1.80 buildActionMask = 2147483647;
1.81 @@ -109,6 +150,7 @@
1.82 08FB7794FE84155DC02AAC07 /* BPlusTree */ = {
1.83 isa = PBXGroup;
1.84 children = (
1.85 + 276E5D081067D20B008A2171 /* Cocoa */,
1.86 276E5BB91066D135008A2171 /* include */,
1.87 276E5BC31066D13D008A2171 /* src */,
1.88 276E5BD61066D142008A2171 /* test */,
1.89 @@ -124,10 +166,27 @@
1.90 children = (
1.91 8DD76F6C0486A84900D96B5E /* OttomanTest */,
1.92 276E5CAE1067315D008A2171 /* libOttoman.a */,
1.93 + 276E5D101067D27E008A2171 /* OttomanCocoa */,
1.94 );
1.95 name = Products;
1.96 sourceTree = "<group>";
1.97 };
1.98 + 2754DDEB106BD38600365FAA /* MYUtilities */ = {
1.99 + isa = PBXGroup;
1.100 + children = (
1.101 + 2754DE2B106BD65600365FAA /* CollectionUtils.h */,
1.102 + 2754DE2C106BD65600365FAA /* CollectionUtils.m */,
1.103 + 2754DDF1106BD3D300365FAA /* ExceptionUtils.h */,
1.104 + 2754DDF2106BD3D300365FAA /* ExceptionUtils.m */,
1.105 + 2754DDEC106BD3BD00365FAA /* Logging.h */,
1.106 + 2754DDED106BD3BD00365FAA /* Logging.m */,
1.107 + 2754DDC3106BD20A00365FAA /* Test.h */,
1.108 + 2754DDC4106BD20A00365FAA /* Test.m */,
1.109 + );
1.110 + name = MYUtilities;
1.111 + path = /Volumes/snoog/Code/MYUtilities;
1.112 + sourceTree = "<absolute>";
1.113 + };
1.114 276E5BB91066D135008A2171 /* include */ = {
1.115 isa = PBXGroup;
1.116 children = (
1.117 @@ -173,6 +232,21 @@
1.118 path = test;
1.119 sourceTree = "<group>";
1.120 };
1.121 + 276E5D081067D20B008A2171 /* Cocoa */ = {
1.122 + isa = PBXGroup;
1.123 + children = (
1.124 + 276E5D091067D24A008A2171 /* MYOttoman.h */,
1.125 + 276E5D0A1067D24A008A2171 /* MYOttoman.mm */,
1.126 + 276E5D2C1067F86E008A2171 /* MYVersionDictionary.h */,
1.127 + 276E5D2D1067F86E008A2171 /* MYVersionDictionary.mm */,
1.128 + 276E5D451069246E008A2171 /* MYOttoman_internal.h */,
1.129 + 2754DD86106BCBCF00365FAA /* MYOttoman_test.m */,
1.130 + 2754DDEB106BD38600365FAA /* MYUtilities */,
1.131 + );
1.132 + name = Cocoa;
1.133 + path = bindings/Cocoa;
1.134 + sourceTree = "<group>";
1.135 + };
1.136 /* End PBXGroup section */
1.137
1.138 /* Begin PBXHeadersBuildPhase section */
1.139 @@ -212,6 +286,23 @@
1.140 productReference = 276E5CAE1067315D008A2171 /* libOttoman.a */;
1.141 productType = "com.apple.product-type.library.static";
1.142 };
1.143 + 276E5D0F1067D27E008A2171 /* Cocoa */ = {
1.144 + isa = PBXNativeTarget;
1.145 + buildConfigurationList = 276E5D161067D2D1008A2171 /* Build configuration list for PBXNativeTarget "Cocoa" */;
1.146 + buildPhases = (
1.147 + 276E5D0D1067D27E008A2171 /* Sources */,
1.148 + 276E5D0E1067D27E008A2171 /* Frameworks */,
1.149 + );
1.150 + buildRules = (
1.151 + );
1.152 + dependencies = (
1.153 + 276E5D7910692FA7008A2171 /* PBXTargetDependency */,
1.154 + );
1.155 + name = Cocoa;
1.156 + productName = Cocoa;
1.157 + productReference = 276E5D101067D27E008A2171 /* OttomanCocoa */;
1.158 + productType = "com.apple.product-type.tool";
1.159 + };
1.160 8DD76F620486A84900D96B5E /* OttomanTest */ = {
1.161 isa = PBXNativeTarget;
1.162 buildConfigurationList = 1DEB923108733DC60010E9CD /* Build configuration list for PBXNativeTarget "OttomanTest" */;
1.163 @@ -244,6 +335,7 @@
1.164 targets = (
1.165 8DD76F620486A84900D96B5E /* OttomanTest */,
1.166 276E5CAD1067315D008A2171 /* Static Library */,
1.167 + 276E5D0F1067D27E008A2171 /* Cocoa */,
1.168 );
1.169 };
1.170 /* End PBXProject section */
1.171 @@ -265,6 +357,20 @@
1.172 );
1.173 runOnlyForDeploymentPostprocessing = 0;
1.174 };
1.175 + 276E5D0D1067D27E008A2171 /* Sources */ = {
1.176 + isa = PBXSourcesBuildPhase;
1.177 + buildActionMask = 2147483647;
1.178 + files = (
1.179 + 276E5D141067D2AD008A2171 /* MYOttoman.mm in Sources */,
1.180 + 276E5D2E1067F86E008A2171 /* MYVersionDictionary.mm in Sources */,
1.181 + 2754DD87106BCBCF00365FAA /* MYOttoman_test.m in Sources */,
1.182 + 2754DDC5106BD20A00365FAA /* Test.m in Sources */,
1.183 + 2754DDEE106BD3BD00365FAA /* Logging.m in Sources */,
1.184 + 2754DDF3106BD3D300365FAA /* ExceptionUtils.m in Sources */,
1.185 + 2754DE2D106BD65600365FAA /* CollectionUtils.m in Sources */,
1.186 + );
1.187 + runOnlyForDeploymentPostprocessing = 0;
1.188 + };
1.189 8DD76F640486A84900D96B5E /* Sources */ = {
1.190 isa = PBXSourcesBuildPhase;
1.191 buildActionMask = 2147483647;
1.192 @@ -288,6 +394,14 @@
1.193 };
1.194 /* End PBXSourcesBuildPhase section */
1.195
1.196 +/* Begin PBXTargetDependency section */
1.197 + 276E5D7910692FA7008A2171 /* PBXTargetDependency */ = {
1.198 + isa = PBXTargetDependency;
1.199 + target = 276E5CAD1067315D008A2171 /* Static Library */;
1.200 + targetProxy = 276E5D7810692FA7008A2171 /* PBXContainerItemProxy */;
1.201 + };
1.202 +/* End PBXTargetDependency section */
1.203 +
1.204 /* Begin XCBuildConfiguration section */
1.205 1DEB923208733DC60010E9CD /* Debug */ = {
1.206 isa = XCBuildConfiguration;
1.207 @@ -320,6 +434,7 @@
1.208 buildSettings = {
1.209 ARCHS = "$(ARCHS_STANDARD_32_BIT)";
1.210 GCC_C_LANGUAGE_STANDARD = c99;
1.211 + GCC_INLINES_ARE_PRIVATE_EXTERN = YES;
1.212 GCC_OPTIMIZATION_LEVEL = 0;
1.213 GCC_PRECOMPILE_PREFIX_HEADER = YES;
1.214 GCC_PREPROCESSOR_DEFINITIONS = "";
1.215 @@ -345,6 +460,7 @@
1.216 GCC_C_LANGUAGE_STANDARD = c99;
1.217 GCC_DYNAMIC_NO_PIC = YES;
1.218 GCC_ENABLE_CPP_RTTI = NO;
1.219 + GCC_INLINES_ARE_PRIVATE_EXTERN = YES;
1.220 GCC_PRECOMPILE_PREFIX_HEADER = YES;
1.221 GCC_PREPROCESSOR_DEFINITIONS = NDEBUG;
1.222 GCC_TREAT_WARNINGS_AS_ERRORS = YES;
1.223 @@ -380,12 +496,43 @@
1.224 buildSettings = {
1.225 COPY_PHASE_STRIP = YES;
1.226 DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
1.227 - GENERATE_MASTER_OBJECT_FILE = YES;
1.228 INSTALL_PATH = /usr/local/lib;
1.229 PRODUCT_NAME = Ottoman;
1.230 };
1.231 name = Release;
1.232 };
1.233 + 276E5D121067D27F008A2171 /* Debug */ = {
1.234 + isa = XCBuildConfiguration;
1.235 + buildSettings = {
1.236 + COPY_PHASE_STRIP = NO;
1.237 + GCC_PRECOMPILE_PREFIX_HEADER = NO;
1.238 + GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/Foundation.framework/Headers/Foundation.h";
1.239 + GCC_PREPROCESSOR_DEFINITIONS = DEBUG;
1.240 + INSTALL_PATH = /usr/local/bin;
1.241 + OTHER_LDFLAGS = (
1.242 + "-framework",
1.243 + Foundation,
1.244 + );
1.245 + PRODUCT_NAME = OttomanCocoa;
1.246 + };
1.247 + name = Debug;
1.248 + };
1.249 + 276E5D131067D27F008A2171 /* Release */ = {
1.250 + isa = XCBuildConfiguration;
1.251 + buildSettings = {
1.252 + COPY_PHASE_STRIP = YES;
1.253 + GCC_PRECOMPILE_PREFIX_HEADER = NO;
1.254 + GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/Foundation.framework/Headers/Foundation.h";
1.255 + GCC_PREPROCESSOR_DEFINITIONS = DEBUG;
1.256 + INSTALL_PATH = /usr/local/bin;
1.257 + OTHER_LDFLAGS = (
1.258 + "-framework",
1.259 + Foundation,
1.260 + );
1.261 + PRODUCT_NAME = OttomanCocoa;
1.262 + };
1.263 + name = Release;
1.264 + };
1.265 /* End XCBuildConfiguration section */
1.266
1.267 /* Begin XCConfigurationList section */
1.268 @@ -416,6 +563,15 @@
1.269 defaultConfigurationIsVisible = 0;
1.270 defaultConfigurationName = Release;
1.271 };
1.272 + 276E5D161067D2D1008A2171 /* Build configuration list for PBXNativeTarget "Cocoa" */ = {
1.273 + isa = XCConfigurationList;
1.274 + buildConfigurations = (
1.275 + 276E5D121067D27F008A2171 /* Debug */,
1.276 + 276E5D131067D27F008A2171 /* Release */,
1.277 + );
1.278 + defaultConfigurationIsVisible = 0;
1.279 + defaultConfigurationName = Release;
1.280 + };
1.281 /* End XCConfigurationList section */
1.282 };
1.283 rootObject = 08FB7793FE84155DC02AAC07 /* Project object */;
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2.2 +++ b/bindings/Cocoa/MYOttoman.h Thu Sep 24 21:47:06 2009 -0700
2.3 @@ -0,0 +1,81 @@
2.4 +//
2.5 +// MYOttoman.h
2.6 +// Ottoman
2.7 +//
2.8 +// Created by Jens Alfke on 9/21/09.
2.9 +// Copyright 2009 Jens Alfke. All rights reserved.
2.10 +//
2.11 +
2.12 +#import <Foundation/Foundation.h>
2.13 +
2.14 +@class MYVersionDictionary, MYCurrentVersionDictionary;
2.15 +
2.16 +
2.17 +/** A version-controlled persistent dictionary.
2.18 + Each version is stored as a MYVersionDictionary,
2.19 + unsaved changes as an MYCurrentVersionDictionary. */
2.20 +@interface MYOttoman : NSObject
2.21 +{
2.22 + void *_ottoman;
2.23 + MYVersionDictionary *_lastVersion;
2.24 + MYCurrentVersionDictionary *_currentVersion;
2.25 +}
2.26 +
2.27 +/** Creates an "untitled" Ottoman with no file and no lastVersion.
2.28 + After adding values to the currentVersion, use saveAs to save it. */
2.29 +- (id) init;
2.30 +
2.31 +/** Opens an existing Ottoman file. */
2.32 +- (id) initWithURL: (NSURL*)fileURL writeable: (BOOL)writeable error: (NSError**)outError;
2.33 +
2.34 +/** Closes an Ottoman. */
2.35 +- (void) close;
2.36 +
2.37 +/** The current file, or nil if the receiver is still in the "untitled" state. */
2.38 +@property (readonly) NSURL *URL;
2.39 +
2.40 +/** The latest saved version of the dictionary.
2.41 + Earlier versions can be accessed through its previousVersion property.
2.42 + This will be nil if the receiver is still in the "untitled" state. */
2.43 +@property (readonly) MYVersionDictionary* lastVersion;
2.44 +
2.45 +/** A mutable overlay representing the current state of the dictionary.
2.46 + Changes are made in memory until -save is called.
2.47 + This will be nil if the receiver was opened read-only (writeable=NO). */
2.48 +@property (readonly) MYCurrentVersionDictionary* currentVersion;
2.49 +
2.50 +/** Has the on-disk file been updated with newer revisions than what I know about? */
2.51 +@property (readonly) BOOL needsSync;
2.52 +
2.53 +/** Reads any newer versions from disk.
2.54 + Returns YES if new versions were read, NO if there were none or on error.
2.55 + Afterwards, -lastVersion will return the latest version in the file.
2.56 + (The old lastVersion dictionary is still around, so you can save a pointer to it
2.57 + before the call and use it later to see what changed.)
2.58 + Changes made to the -currentVersion dictionary are not lost, but are now relative
2.59 + to the new lastVersion. You may want to scan them and resolve conflicts. */
2.60 +- (BOOL) sync: (NSError**)outError;
2.61 +
2.62 +/** Saves the current version to the file, by appending.
2.63 + Returns NO if there is a version conflict; in that case you need to call -sync,
2.64 + possibly resolve conflicts, and then call -save again. */
2.65 +- (BOOL) save: (NSError**)outError;
2.66 +
2.67 +/** Saves the current version to the file, by writing to a new temporary file and
2.68 + then atomically replacing the original.
2.69 + Older versions, and older copies of the data, are not preserved, so this
2.70 + will typically shrink the file quite a bit.
2.71 + Returns NO if there is a version conflict. */
2.72 +- (BOOL) saveAndCompact: (NSError**)outError;
2.73 +
2.74 +/** Saves the current version to a new file, leaving the new file open,
2.75 + so subsequent saves will be written to it. */
2.76 +- (BOOL) saveAs: (NSURL*)newFileURL
2.77 + overwriteAllowed: (BOOL)overwriteAllowed
2.78 + error: (NSError**)outError;
2.79 +
2.80 +/** Scans the file for damage. Returns YES if the file is OK, NO if problems are found.
2.81 + If the 'repair' flag is set, will truncate the file to the last valid version. */
2.82 +- (BOOL) scavengeAndRepair: (BOOL)repair error: (NSError**)outError;
2.83 +
2.84 +@end
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3.2 +++ b/bindings/Cocoa/MYOttoman.mm Thu Sep 24 21:47:06 2009 -0700
3.3 @@ -0,0 +1,205 @@
3.4 +//
3.5 +// MYOttoman.mm
3.6 +// Ottoman
3.7 +//
3.8 +// Created by Jens Alfke on 9/21/09.
3.9 +// Copyright 2009 Jens Alfke. All rights reserved.
3.10 +//
3.11 +
3.12 +#import "MYOttoman.h"
3.13 +#import "MYOttoman_internal.h"
3.14 +extern "C" {
3.15 +#import "Test.h"
3.16 +}
3.17 +
3.18 +#include "Ottoman.h"
3.19 +#include "VersionDictionary.h"
3.20 +#include "File.h"
3.21 +
3.22 +
3.23 +@interface MYOttoman ()
3.24 +- (void) _versionsChanged;
3.25 +@end
3.26 +
3.27 +
3.28 +namespace Mooseyard {
3.29 +
3.30 + class ObjCOwnedOttoman :public Ottoman {
3.31 + public:
3.32 + ObjCOwnedOttoman (MYOttoman *owner)
3.33 + :_owner(owner)
3.34 + { }
3.35 +
3.36 + ObjCOwnedOttoman (MYOttoman *owner, NSURL *fileURL, bool writeable)
3.37 + :Ottoman(fileURL.path.fileSystemRepresentation, writeable),
3.38 + _owner(owner)
3.39 + { }
3.40 +
3.41 + protected:
3.42 + virtual void versionsChanged() {
3.43 + [_owner _versionsChanged];
3.44 + }
3.45 + private:
3.46 + MYOttoman *_owner;
3.47 + };
3.48 +
3.49 +}
3.50 +
3.51 +
3.52 +using namespace Mooseyard;
3.53 +
3.54 +
3.55 +static BOOL ErrorToNSError (const File::Error &x, NSError **outError) {
3.56 + if (outError) {
3.57 + *outError = [NSError errorWithDomain: NSPOSIXErrorDomain
3.58 + code: x.code
3.59 + userInfo: nil];
3.60 + }
3.61 + return NO;
3.62 +}
3.63 +
3.64 +
3.65 +@interface MYOttoman ()
3.66 +@property (readonly) Ottoman* ottoman;
3.67 +@end
3.68 +
3.69 +
3.70 +@implementation MYOttoman
3.71 +
3.72 +
3.73 +- (id) init {
3.74 + return [self initWithURL: nil writeable: YES error: nil];
3.75 +}
3.76 +
3.77 +- (id) initWithURL: (NSURL*)fileURL
3.78 + writeable: (BOOL)writeable
3.79 + error: (NSError**)outError
3.80 +{
3.81 + self = [super init];
3.82 + if (self) {
3.83 + try {
3.84 + if (fileURL) {
3.85 + NSAssert([fileURL isFileURL], @"MYOttoman only supports file: URLs");
3.86 + _ottoman = new ObjCOwnedOttoman(self, fileURL, writeable);
3.87 + } else {
3.88 + _ottoman = new ObjCOwnedOttoman(self);
3.89 + }
3.90 + } catch (const File::Error &x) {
3.91 + ErrorToNSError(x,outError);
3.92 + [self release];
3.93 + return nil;
3.94 + }
3.95 +
3.96 + if (writeable)
3.97 + _currentVersion = [[MYCurrentVersionDictionary alloc]
3.98 + _initWithOverlayDictionary: self.ottoman->currentVersion()];
3.99 + }
3.100 + return self;
3.101 +}
3.102 +
3.103 +- (void) dealloc
3.104 +{
3.105 + [_lastVersion release];
3.106 + [_currentVersion release];
3.107 + delete (Ottoman*)_ottoman;
3.108 + [super dealloc];
3.109 +}
3.110 +
3.111 +- (void) finalize {
3.112 + delete (Ottoman*)_ottoman;
3.113 + [super finalize];
3.114 +}
3.115 +
3.116 +- (void) close {
3.117 + delete (Ottoman*)_ottoman;
3.118 + _ottoman = nil;
3.119 +}
3.120 +
3.121 +
3.122 +- (Ottoman*) ottoman {
3.123 + Assert(_ottoman, @"MYOttoman has already been closed");
3.124 + return (Ottoman*) _ottoman;
3.125 +}
3.126 +
3.127 +- (NSURL*) URL {
3.128 + const char *path = self.ottoman->filename();
3.129 + return path ?[NSURL fileURLWithPath: [NSString stringWithUTF8String: path]] :nil;
3.130 +}
3.131 +
3.132 +
3.133 +- (MYVersionDictionary*) lastVersion {
3.134 + if (!_lastVersion)
3.135 + _lastVersion = [[MYVersionDictionary alloc] _initWithVersionDictionary: self.ottoman->lastVersion()];
3.136 + return _lastVersion;
3.137 +}
3.138 +
3.139 +- (MYCurrentVersionDictionary*) currentVersion {
3.140 + return _currentVersion;
3.141 +}
3.142 +
3.143 +- (void) _versionsChanged {
3.144 + [_lastVersion autorelease];
3.145 + _lastVersion = nil;
3.146 +}
3.147 +
3.148 +
3.149 +- (BOOL) needsSync {
3.150 + return self.ottoman->needsSync();
3.151 +}
3.152 +
3.153 +
3.154 +- (BOOL) sync: (NSError**)outError {
3.155 + if (outError) *outError = nil;
3.156 + try {
3.157 + return self.ottoman->sync();
3.158 + } catch (const File::Error &x) {
3.159 + return ErrorToNSError(x,outError);
3.160 + }
3.161 +}
3.162 +
3.163 +- (BOOL) save: (NSError**)outError {
3.164 + if (outError) *outError = nil;
3.165 + try {
3.166 + return self.ottoman->save();
3.167 + } catch (const File::Error &x) {
3.168 + return ErrorToNSError(x,outError);
3.169 + }
3.170 +}
3.171 +
3.172 +- (BOOL) saveAndCompact: (NSError**)outError {
3.173 + if (outError) *outError = nil;
3.174 + try {
3.175 + return self.ottoman->saveAndCompact();
3.176 + } catch (const File::Error &x) {
3.177 + return ErrorToNSError(x,outError);
3.178 + }
3.179 +}
3.180 +
3.181 +- (BOOL) saveAs: (NSURL*)newFileURL
3.182 + overwriteAllowed: (BOOL)overwriteAllowed
3.183 + error: (NSError**)outError
3.184 +{
3.185 + NSParameterAssert(newFileURL!=nil);
3.186 + NSAssert([newFileURL isFileURL], @"MYOttoman only supports file: URLs");
3.187 + if (outError) *outError = nil;
3.188 + try {
3.189 + self.ottoman->saveAs(newFileURL.path.fileSystemRepresentation, overwriteAllowed);
3.190 + return YES;
3.191 + } catch (const File::Error &x) {
3.192 + return ErrorToNSError(x,outError);
3.193 + }
3.194 +}
3.195 +
3.196 +
3.197 +- (BOOL) scavengeAndRepair: (BOOL)repair error: (NSError**)outError {
3.198 + if (outError) *outError = nil;
3.199 + try {
3.200 + return self.ottoman->scavenge(repair);
3.201 + } catch (const File::Error &x) {
3.202 + return ErrorToNSError(x,outError);
3.203 + }
3.204 +}
3.205 +
3.206 +
3.207 +@end
3.208 +
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
4.2 +++ b/bindings/Cocoa/MYOttoman_internal.h Thu Sep 24 21:47:06 2009 -0700
4.3 @@ -0,0 +1,23 @@
4.4 +//
4.5 +// MYOttoman_internal
4.6 +// Ottoman
4.7 +//
4.8 +// Created by Jens Alfke on 9/22/09.
4.9 +// Copyright 2009 Jens Alfke. All rights reserved.
4.10 +//
4.11 +
4.12 +
4.13 +#import "MYVersionDictionary.h"
4.14 +
4.15 +namespace Mooseyard {
4.16 + class OverlayDictionary;
4.17 + class VersionDictionary;
4.18 +}
4.19 +
4.20 +@interface MYVersionDictionary ()
4.21 +- (id) _initWithVersionDictionary: (const Mooseyard::VersionDictionary*)dict;
4.22 +@end
4.23 +
4.24 +@interface MYCurrentVersionDictionary ()
4.25 +- (id) _initWithOverlayDictionary: (Mooseyard::OverlayDictionary*)dict;
4.26 +@end
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
5.2 +++ b/bindings/Cocoa/MYOttoman_test.m Thu Sep 24 21:47:06 2009 -0700
5.3 @@ -0,0 +1,121 @@
5.4 +//
5.5 +// MYOttoman_test.m
5.6 +// Ottoman
5.7 +//
5.8 +// Created by Jens Alfke on 9/24/09.
5.9 +// Copyright 2009 Jens Alfke. All rights reserved.
5.10 +//
5.11 +
5.12 +#import "MYOttoman.h"
5.13 +#import "MYVersionDictionary.h"
5.14 +
5.15 +#import "Test.h"
5.16 +
5.17 +static NSData* dataOf (NSString *str) {
5.18 + return [str dataUsingEncoding: NSUTF8StringEncoding];
5.19 +}
5.20 +
5.21 +/*
5.22 +static NSString* stringOf (id data) {
5.23 + if (!data)
5.24 + return nil;
5.25 + CAssert([data isKindOfClass: [NSData class]], @"stringOf expected NSData, got %@", [data class]);
5.26 + NSString *str = [[[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding] autorelease];
5.27 + CAssert(str!=nil, @"Bad data (couldn't decode UTF-8): %@", data);
5.28 + return str;
5.29 +}
5.30 +*/
5.31 +
5.32 +static void checkFile (NSString *path) {
5.33 + NSError *error;
5.34 + NSDictionary *fileInfo = [[NSFileManager defaultManager] attributesOfItemAtPath: path error: &error];
5.35 + CAssert(fileInfo, @"Couldn't get info for %@: %@", path,error);
5.36 + CAssert([fileInfo fileSize] >= 512, @"Unexpected file size %lld", [fileInfo fileSize]);
5.37 + NSTimeInterval age = - [[fileInfo fileModificationDate] timeIntervalSinceNow];
5.38 + CAssert(age < 60.0, @"File %@ is too old: %.1lf sec", path,age);
5.39 +}
5.40 +
5.41 +
5.42 +TestCase(CreateMYOttoman) {
5.43 + MYOttoman *o = [[MYOttoman alloc] init];
5.44 + MYCurrentVersionDictionary *cur = o.currentVersion;
5.45 + CAssertEq(o.lastVersion,nil);
5.46 +
5.47 + [cur setObject: dataOf(@"first value") forKey: dataOf(@"first key")];
5.48 + [cur setObject: dataOf(@"second value") forKey: dataOf(@"second key")];
5.49 + [cur setObject: dataOf(@"third value") forKey: dataOf(@"third key")];
5.50 + [cur removeObjectForKey: dataOf(@"third key")];
5.51 + [cur removeObjectForKey: dataOf(@"bogus")];
5.52 +
5.53 + CAssertEq(cur.count, 2);
5.54 + CAssertEqual([cur objectForKey: dataOf(@"first key")], dataOf(@"first value"));
5.55 + CAssertEqual([cur objectForKey: dataOf(@"second key")], dataOf(@"second value"));
5.56 + CAssertEqual([cur objectForKey: dataOf(@"third key")], nil);
5.57 +
5.58 + NSError *error;
5.59 + if (![o saveAs: [NSURL fileURLWithPath: @"/tmp/myottomantest.ottoman"]
5.60 + overwriteAllowed: YES error: &error])
5.61 + CAssert(NO, @"saveAs: failed: %@", error);
5.62 + checkFile(@"/tmp/myottomantest.ottoman");
5.63 +
5.64 + MYVersionDictionary *last = o.lastVersion;
5.65 + CAssert(last, @"lastVersion is nil after save");
5.66 + CAssertEq(last.generation,0);
5.67 + CAssertEqual([last objectForKey: dataOf(@"first key")], dataOf(@"first value"));
5.68 + CAssertEqual([last objectForKey: dataOf(@"second key")], dataOf(@"second value"));
5.69 + CAssertEqual([last objectForKey: dataOf(@"third key")], nil);
5.70 +
5.71 + [cur setObject: dataOf(@"fourth value") forKey: dataOf(@"fourth key")];
5.72 + [cur setObject: dataOf(@"new first value") forKey: dataOf(@"first key")];
5.73 + [cur removeObjectForKey: dataOf(@"second key")];
5.74 + CAssertEq(cur.count, 2);
5.75 + CAssertEqual([cur objectForKey: dataOf(@"first key")], dataOf(@"new first value"));
5.76 + CAssertEqual([cur objectForKey: dataOf(@"second key")], nil);
5.77 + CAssertEqual([cur objectForKey: dataOf(@"fourth key")], dataOf(@"fourth value"));
5.78 +
5.79 + if (![o save: &error])
5.80 + CAssert(NO, @"save: failed: %@", error);
5.81 +
5.82 + CAssertEqual([last objectForKey: dataOf(@"first key")], dataOf(@"first value"));
5.83 + CAssertEqual([last objectForKey: dataOf(@"second key")], dataOf(@"second value"));
5.84 + CAssertEqual([last objectForKey: dataOf(@"third key")], nil);
5.85 +
5.86 + last = o.lastVersion;
5.87 + CAssertEq(last.generation,1);
5.88 + CAssertEqual([last objectForKey: dataOf(@"first key")], dataOf(@"new first value"));
5.89 + CAssertEqual([last objectForKey: dataOf(@"second key")], nil);
5.90 + CAssertEqual([last objectForKey: dataOf(@"third key")], nil);
5.91 + CAssertEqual([last objectForKey: dataOf(@"fourth key")], dataOf(@"fourth value"));
5.92 +
5.93 + [o close];
5.94 + [o release];
5.95 +
5.96 + o = [[MYOttoman alloc] initWithURL: [NSURL fileURLWithPath: @"/tmp/myottomantest.ottoman"]
5.97 + writeable: NO error: &error];
5.98 + CAssert(o, @"Failed to re-open Ottoman: %@", error);
5.99 + CAssertEq(o.currentVersion, nil);
5.100 + last = o.lastVersion;
5.101 + CAssertEq(last.generation,1);
5.102 + CAssertEqual([last objectForKey: dataOf(@"first key")], dataOf(@"new first value"));
5.103 + CAssertEqual([last objectForKey: dataOf(@"second key")], nil);
5.104 + CAssertEqual([last objectForKey: dataOf(@"third key")], nil);
5.105 + CAssertEqual([last objectForKey: dataOf(@"fourth key")], dataOf(@"fourth value"));
5.106 +
5.107 + MYVersionDictionary *prev = last.previousVersion;
5.108 + CAssert(prev!=nil);
5.109 + CAssertEq(prev.generation,0);
5.110 + CAssertEq(prev.previousVersion,nil);
5.111 + CAssertEqual([prev objectForKey: dataOf(@"first key")], dataOf(@"first value"));
5.112 + CAssertEqual([prev objectForKey: dataOf(@"second key")], dataOf(@"second value"));
5.113 + CAssertEqual([prev objectForKey: dataOf(@"third key")], nil);
5.114 +
5.115 + [o close];
5.116 + [o release];
5.117 +}
5.118 +
5.119 +
5.120 +int main(int argc, const char**argv) {
5.121 + const char* testArgs[2] = {"", "Test_All"};
5.122 + RunTestCases(2,testArgs);
5.123 + return 0;
5.124 +}
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
6.2 +++ b/bindings/Cocoa/MYVersionDictionary.h Thu Sep 24 21:47:06 2009 -0700
6.3 @@ -0,0 +1,55 @@
6.4 +//
6.5 +// MYVersionDictionary.h
6.6 +// Ottoman
6.7 +//
6.8 +// Created by Jens Alfke on 9/21/09.
6.9 +// Copyright 2009 Jens Alfke. All rights reserved.
6.10 +//
6.11 +
6.12 +#import <Foundation/NSDictionary.h>
6.13 +
6.14 +#ifdef __cplusplus
6.15 +namespace Mooseyard {
6.16 + class OverlayDictionary;
6.17 + class VersionDictionary;
6.18 +}
6.19 +#endif
6.20 +
6.21 +
6.22 +/** A persistent NSDictionary embedded in a memory-mapped file,
6.23 + representing one complete version of a MYOttoman dictionary. */
6.24 +@interface MYVersionDictionary : NSDictionary
6.25 +{
6.26 +#ifdef __cplusplus
6.27 + const Mooseyard::VersionDictionary *_dict;
6.28 +#else
6.29 + void *_dict;
6.30 +#endif
6.31 +}
6.32 +
6.33 +/** The generation number. The first version in a new file is zero. */
6.34 +@property (readonly) int generation;
6.35 +
6.36 +/** The time at which this version was written to the file. */
6.37 +@property (readonly) NSDate *dateCreated;
6.38 +
6.39 +/** The previous version, before this one was saved. */
6.40 +@property (readonly) MYVersionDictionary *previousVersion;
6.41 +
6.42 +/** Is this the latest version in the file? */
6.43 +@property (readonly) BOOL isLatestVersion;
6.44 +
6.45 +@end
6.46 +
6.47 +
6.48 +
6.49 +@interface MYCurrentVersionDictionary : NSMutableDictionary
6.50 +{
6.51 +#ifdef __cplusplus
6.52 + Mooseyard::OverlayDictionary *_dict;
6.53 +#else
6.54 + void *_dict;
6.55 +#endif
6.56 +}
6.57 +
6.58 +@end
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
7.2 +++ b/bindings/Cocoa/MYVersionDictionary.mm Thu Sep 24 21:47:06 2009 -0700
7.3 @@ -0,0 +1,187 @@
7.4 +//
7.5 +// MYVersionDictionary.m
7.6 +// Ottoman
7.7 +//
7.8 +// Created by Jens Alfke on 9/21/09.
7.9 +// Copyright 2009 Jens Alfke. All rights reserved.
7.10 +//
7.11 +
7.12 +#import "MYVersionDictionary.h"
7.13 +#import "MYOttoman_internal.h"
7.14 +#import <Foundation/Foundation.h>
7.15 +
7.16 +#include "VersionDictionary.h"
7.17 +
7.18 +using namespace Mooseyard;
7.19 +
7.20 +
7.21 +@interface MYDictionaryEnumerator : NSEnumerator
7.22 +{
7.23 + Dictionary::Iterator *_iter;
7.24 + BOOL _started;
7.25 +}
7.26 +- (id) initWithDict: (const Dictionary*)dict;
7.27 +@end
7.28 +
7.29 +
7.30 +
7.31 +static id BlobToNSData (const Blob &blob, bool copy =true) {
7.32 + if (!blob)
7.33 + return nil;
7.34 + else if (copy)
7.35 + return [NSData dataWithBytes: blob.bytes length: blob.length];
7.36 + else
7.37 + return [NSData dataWithBytesNoCopy: (void*)blob.bytes
7.38 + length: blob.length
7.39 + freeWhenDone: NO];
7.40 +}
7.41 +
7.42 +static Blob BlobFromId (id object) {
7.43 + if ([object isKindOfClass: [NSData class]])
7.44 + return Blob([object bytes], [object length]);
7.45 + else
7.46 + return Key();
7.47 +}
7.48 +
7.49 +
7.50 +
7.51 +@implementation MYVersionDictionary
7.52 +
7.53 +
7.54 +- (id) _initWithVersionDictionary: (VersionDictionary*)dict
7.55 +{
7.56 + self = [super init];
7.57 + if (self != nil) {
7.58 + if (!dict) {
7.59 + [self release];
7.60 + return nil;
7.61 + }
7.62 + _dict = dict;
7.63 + }
7.64 + return self;
7.65 +}
7.66 +
7.67 +
7.68 +- (NSUInteger)count {
7.69 + return _dict->count();
7.70 +}
7.71 +
7.72 +- (id)objectForKey:(id)keyObject {
7.73 + return BlobToNSData( _dict->get( Key(BlobFromId(keyObject)) ) );
7.74 +}
7.75 +
7.76 +- (NSEnumerator *)keyEnumerator {
7.77 + return [[[MYDictionaryEnumerator alloc] initWithDict: _dict] autorelease];
7.78 +}
7.79 +
7.80 +- (int) generation {
7.81 + return _dict->generation();
7.82 +}
7.83 +
7.84 +- (NSDate*) dateCreated {
7.85 + return [NSDate dateWithTimeIntervalSince1970: _dict->timestamp()];
7.86 +}
7.87 +
7.88 +- (BOOL) isLatestVersion {
7.89 + return _dict->isLatestVersion();
7.90 +}
7.91 +
7.92 +- (MYVersionDictionary*) previousVersion {
7.93 + const VersionDictionary *prev = _dict->previousVersion();
7.94 + if (prev)
7.95 + return [[[MYVersionDictionary alloc] _initWithVersionDictionary: prev] autorelease];
7.96 + else
7.97 + return nil;
7.98 +}
7.99 +
7.100 +@end
7.101 +
7.102 +
7.103 +
7.104 +
7.105 +@implementation MYCurrentVersionDictionary
7.106 +
7.107 +
7.108 +- (id) _initWithOverlayDictionary: (OverlayDictionary*)dict
7.109 +{
7.110 + self = [super init];
7.111 + if (self != nil) {
7.112 + if (!dict) {
7.113 + [self release];
7.114 + return nil;
7.115 + }
7.116 + _dict = dict;
7.117 + }
7.118 + return self;
7.119 +}
7.120 +
7.121 +
7.122 +- (NSUInteger)count {
7.123 + return _dict->count();
7.124 +}
7.125 +
7.126 +- (id)objectForKey:(id)keyObject {
7.127 + Blob keyBlob = BlobFromId(keyObject);
7.128 + return keyBlob ? BlobToNSData( _dict->get( Key(keyBlob) ) ) :nil;
7.129 +}
7.130 +
7.131 +- (NSEnumerator *)keyEnumerator {
7.132 + return [[[MYDictionaryEnumerator alloc] initWithDict: _dict] autorelease];
7.133 +}
7.134 +
7.135 +- (void)setObject:(id)anObject forKey:(id)aKey {
7.136 + Key key = Key(BlobFromId(aKey));
7.137 + NSAssert1(key, @"Invalid %@ key; only NSData supported", [aKey class]);
7.138 + Blob value = BlobFromId(anObject);
7.139 + NSAssert1(key, @"Invalid %@ value; only NSData supported", [aKey class]);
7.140 + _dict->put(key, value);
7.141 +}
7.142 +
7.143 +- (void)removeObjectForKey:(id)aKey {
7.144 + Key key = Key(BlobFromId(aKey));
7.145 + NSAssert1(key, @"Invalid key class %@; only NSData supported", [aKey class]);
7.146 + _dict->remove(key);
7.147 +}
7.148 +
7.149 +@end
7.150 +
7.151 +
7.152 +
7.153 +
7.154 +@implementation MYDictionaryEnumerator
7.155 +
7.156 +- (id) initWithDict: (Dictionary*)dict
7.157 +{
7.158 + self = [super init];
7.159 + if (self != nil) {
7.160 + _iter = dict->iterate();
7.161 + }
7.162 + return self;
7.163 +}
7.164 +
7.165 +- (void) dealloc
7.166 +{
7.167 + delete _iter;
7.168 + [super dealloc];
7.169 +}
7.170 +
7.171 +- (void) finalize {
7.172 + delete _iter;
7.173 + [super finalize];
7.174 +}
7.175 +
7.176 +- (id)nextObject {
7.177 + if (_started) {
7.178 + if (!_iter->next())
7.179 + return nil;
7.180 + } else {
7.181 + if (!_iter->hasValue())
7.182 + return nil;
7.183 + _started = YES;
7.184 + }
7.185 + Key key = _iter->key();
7.186 + return [NSData dataWithBytes: key.bytes length: key.length];
7.187 +}
7.188 +
7.189 +
7.190 +@end