mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-23 06:35:51 +00:00
no message
This commit is contained in:
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -1,3 +1,6 @@
|
||||
[submodule "submodules/SQLite.swift"]
|
||||
path = submodules/SQLite.swift
|
||||
url = git@github.com:peter-iakovlev/SQLite.swift.git
|
||||
[submodule "submodules/lmdb"]
|
||||
path = submodules/lmdb
|
||||
url = https://github.com/LMDB/lmdb.git
|
||||
|
||||
@@ -8,9 +8,16 @@
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
D003E4E61B38DBDB00C22CBC /* MessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D003E4E51B38DBDB00C22CBC /* MessageView.swift */; };
|
||||
D00E0FB31B84CEDA002E4EB5 /* Display.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D00E0FB21B84CEDA002E4EB5 /* Display.framework */; };
|
||||
D00E0FB81B85D192002E4EB5 /* lmdb.h in Headers */ = {isa = PBXBuildFile; fileRef = D00E0FB41B85D192002E4EB5 /* lmdb.h */; };
|
||||
D00E0FB91B85D192002E4EB5 /* mdb.c in Sources */ = {isa = PBXBuildFile; fileRef = D00E0FB51B85D192002E4EB5 /* mdb.c */; };
|
||||
D00E0FBA1B85D192002E4EB5 /* midl.c in Sources */ = {isa = PBXBuildFile; fileRef = D00E0FB61B85D192002E4EB5 /* midl.c */; };
|
||||
D00E0FBB1B85D192002E4EB5 /* midl.h in Headers */ = {isa = PBXBuildFile; fileRef = D00E0FB71B85D192002E4EB5 /* midl.h */; };
|
||||
D00E0FBE1B85D1B5002E4EB5 /* LmdbValueBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00E0FBD1B85D1B5002E4EB5 /* LmdbValueBox.swift */; };
|
||||
D044E15E1B2ACB9C001EE087 /* CodingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D044E15D1B2ACB9C001EE087 /* CodingTests.swift */; };
|
||||
D044E1631B2AD677001EE087 /* MurMurHash32.m in Sources */ = {isa = PBXBuildFile; fileRef = D044E1621B2AD677001EE087 /* MurMurHash32.m */; };
|
||||
D044E1641B2AD718001EE087 /* MurMurHash32.h in Headers */ = {isa = PBXBuildFile; fileRef = D044E1611B2AD667001EE087 /* MurMurHash32.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
D055BD331B7D3D2D00F06C0A /* MediaBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = D055BD321B7D3D2D00F06C0A /* MediaBox.swift */; };
|
||||
D07516441B2D9CEF00AE42E0 /* sqlite3.c in Sources */ = {isa = PBXBuildFile; fileRef = D07516401B2D9CEF00AE42E0 /* sqlite3.c */; };
|
||||
D07516451B2D9CEF00AE42E0 /* sqlite3.h in Headers */ = {isa = PBXBuildFile; fileRef = D07516411B2D9CEF00AE42E0 /* sqlite3.h */; };
|
||||
D07516461B2D9CEF00AE42E0 /* sqlite3ext.h in Headers */ = {isa = PBXBuildFile; fileRef = D07516421B2D9CEF00AE42E0 /* sqlite3ext.h */; };
|
||||
@@ -22,13 +29,17 @@
|
||||
D07516771B2EC90400AE42E0 /* fts3_tokenizer.h in Headers */ = {isa = PBXBuildFile; fileRef = D07516741B2EC90400AE42E0 /* fts3_tokenizer.h */; };
|
||||
D07516781B2EC90400AE42E0 /* SQLite-Bridging.h in Headers */ = {isa = PBXBuildFile; fileRef = D07516751B2EC90400AE42E0 /* SQLite-Bridging.h */; };
|
||||
D07516791B2EC90400AE42E0 /* SQLite-Bridging.m in Sources */ = {isa = PBXBuildFile; fileRef = D07516761B2EC90400AE42E0 /* SQLite-Bridging.m */; };
|
||||
D0977F9C1B822DB4009994B2 /* ValueBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0977F9B1B822DB4009994B2 /* ValueBox.swift */; };
|
||||
D0977F9E1B8234DF009994B2 /* ValueBoxKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0977F9D1B8234DF009994B2 /* ValueBoxKey.swift */; };
|
||||
D0977FA01B8244D7009994B2 /* SqliteValueBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0977F9F1B8244D7009994B2 /* SqliteValueBox.swift */; };
|
||||
D0977FA21B82930C009994B2 /* PostboxCodingUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0977FA11B82930C009994B2 /* PostboxCodingUtils.swift */; };
|
||||
D0977FA41B829F87009994B2 /* PostboxTables.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0977FA31B829F87009994B2 /* PostboxTables.swift */; };
|
||||
D0B76BE71B66639F0095CF45 /* DeferredString.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B76BE61B66639F0095CF45 /* DeferredString.swift */; };
|
||||
D0C07F6A1B67DB4800966E43 /* SwiftSignalKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0C07F691B67DB4800966E43 /* SwiftSignalKit.framework */; };
|
||||
D0D224F21B4D6ABD0085E26D /* Functions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D224ED1B4D6ABD0085E26D /* Functions.swift */; };
|
||||
D0D225261B4D84930085E26D /* PeerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D225251B4D84930085E26D /* PeerView.swift */; };
|
||||
D0E3A7501B28A7E300A402D9 /* Postbox.h in Headers */ = {isa = PBXBuildFile; fileRef = D0E3A74F1B28A7E300A402D9 /* Postbox.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
D0E3A7561B28A7E300A402D9 /* Postbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0E3A74A1B28A7E300A402D9 /* Postbox.framework */; };
|
||||
D0E3A75D1B28A7E300A402D9 /* PostboxTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E3A75C1B28A7E300A402D9 /* PostboxTests.swift */; };
|
||||
D0E3A7821B28ADD000A402D9 /* Postbox.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E3A7811B28ADD000A402D9 /* Postbox.swift */; };
|
||||
D0E3A7841B28AE0900A402D9 /* Peer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E3A7831B28AE0900A402D9 /* Peer.swift */; };
|
||||
D0E3A7881B28AE9C00A402D9 /* Coding.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E3A7871B28AE9C00A402D9 /* Coding.swift */; };
|
||||
@@ -48,9 +59,16 @@
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
D003E4E51B38DBDB00C22CBC /* MessageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageView.swift; sourceTree = "<group>"; };
|
||||
D00E0FB21B84CEDA002E4EB5 /* Display.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Display.framework; path = "../../../../Library/Developer/Xcode/DerivedData/Telegram-iOS-gbpsmqzuwcmmxadrqcwyrluaftwp/Build/Products/Debug-iphoneos/Display.framework"; sourceTree = "<group>"; };
|
||||
D00E0FB41B85D192002E4EB5 /* lmdb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = lmdb.h; path = submodules/lmdb/libraries/liblmdb/lmdb.h; sourceTree = SOURCE_ROOT; };
|
||||
D00E0FB51B85D192002E4EB5 /* mdb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = mdb.c; path = submodules/lmdb/libraries/liblmdb/mdb.c; sourceTree = SOURCE_ROOT; };
|
||||
D00E0FB61B85D192002E4EB5 /* midl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = midl.c; path = submodules/lmdb/libraries/liblmdb/midl.c; sourceTree = SOURCE_ROOT; };
|
||||
D00E0FB71B85D192002E4EB5 /* midl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = midl.h; path = submodules/lmdb/libraries/liblmdb/midl.h; sourceTree = SOURCE_ROOT; };
|
||||
D00E0FBD1B85D1B5002E4EB5 /* LmdbValueBox.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LmdbValueBox.swift; sourceTree = "<group>"; };
|
||||
D044E15D1B2ACB9C001EE087 /* CodingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CodingTests.swift; sourceTree = "<group>"; };
|
||||
D044E1611B2AD667001EE087 /* MurMurHash32.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MurMurHash32.h; sourceTree = "<group>"; };
|
||||
D044E1621B2AD677001EE087 /* MurMurHash32.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MurMurHash32.m; sourceTree = "<group>"; };
|
||||
D055BD321B7D3D2D00F06C0A /* MediaBox.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaBox.swift; sourceTree = "<group>"; };
|
||||
D07516401B2D9CEF00AE42E0 /* sqlite3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sqlite3.c; sourceTree = "<group>"; };
|
||||
D07516411B2D9CEF00AE42E0 /* sqlite3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sqlite3.h; sourceTree = "<group>"; };
|
||||
D07516421B2D9CEF00AE42E0 /* sqlite3ext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sqlite3ext.h; sourceTree = "<group>"; };
|
||||
@@ -63,6 +81,11 @@
|
||||
D07516741B2EC90400AE42E0 /* fts3_tokenizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fts3_tokenizer.h; sourceTree = "<group>"; };
|
||||
D07516751B2EC90400AE42E0 /* SQLite-Bridging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "SQLite-Bridging.h"; sourceTree = "<group>"; };
|
||||
D07516761B2EC90400AE42E0 /* SQLite-Bridging.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "SQLite-Bridging.m"; sourceTree = "<group>"; };
|
||||
D0977F9B1B822DB4009994B2 /* ValueBox.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValueBox.swift; sourceTree = "<group>"; };
|
||||
D0977F9D1B8234DF009994B2 /* ValueBoxKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValueBoxKey.swift; sourceTree = "<group>"; };
|
||||
D0977F9F1B8244D7009994B2 /* SqliteValueBox.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SqliteValueBox.swift; sourceTree = "<group>"; };
|
||||
D0977FA11B82930C009994B2 /* PostboxCodingUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PostboxCodingUtils.swift; sourceTree = "<group>"; };
|
||||
D0977FA31B829F87009994B2 /* PostboxTables.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PostboxTables.swift; sourceTree = "<group>"; };
|
||||
D0B76BE61B66639F0095CF45 /* DeferredString.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeferredString.swift; sourceTree = "<group>"; };
|
||||
D0C07F691B67DB4800966E43 /* SwiftSignalKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftSignalKit.framework; path = "../../../../Library/Developer/Xcode/DerivedData/Telegram-iOS-gbpsmqzuwcmmxadrqcwyrluaftwp/Build/Products/Debug-iphoneos/SwiftSignalKit.framework"; sourceTree = "<group>"; };
|
||||
D0D224ED1B4D6ABD0085E26D /* Functions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Functions.swift; path = submodules/sqlite.swift/SQLite/Functions.swift; sourceTree = SOURCE_ROOT; };
|
||||
@@ -72,7 +95,6 @@
|
||||
D0E3A74F1B28A7E300A402D9 /* Postbox.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Postbox.h; sourceTree = "<group>"; };
|
||||
D0E3A7551B28A7E300A402D9 /* PostboxTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PostboxTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
D0E3A75B1B28A7E300A402D9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
D0E3A75C1B28A7E300A402D9 /* PostboxTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostboxTests.swift; sourceTree = "<group>"; };
|
||||
D0E3A7811B28ADD000A402D9 /* Postbox.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Postbox.swift; sourceTree = "<group>"; };
|
||||
D0E3A7831B28AE0900A402D9 /* Peer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Peer.swift; sourceTree = "<group>"; };
|
||||
D0E3A7871B28AE9C00A402D9 /* Coding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Coding.swift; sourceTree = "<group>"; };
|
||||
@@ -85,6 +107,7 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
D00E0FB31B84CEDA002E4EB5 /* Display.framework in Frameworks */,
|
||||
D0C07F6A1B67DB4800966E43 /* SwiftSignalKit.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -100,9 +123,21 @@
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
D00E0FBC1B85D194002E4EB5 /* lmdb */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D00E0FB41B85D192002E4EB5 /* lmdb.h */,
|
||||
D00E0FB51B85D192002E4EB5 /* mdb.c */,
|
||||
D00E0FB61B85D192002E4EB5 /* midl.c */,
|
||||
D00E0FB71B85D192002E4EB5 /* midl.h */,
|
||||
);
|
||||
name = lmdb;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D07515FC1B2C44A200AE42E0 /* thirdparty */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D00E0FBC1B85D194002E4EB5 /* lmdb */,
|
||||
D07516731B2EC8C700AE42E0 /* sqlite.swift */,
|
||||
);
|
||||
name = thirdparty;
|
||||
@@ -144,6 +179,7 @@
|
||||
D0E3A7401B28A7E300A402D9 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D00E0FB21B84CEDA002E4EB5 /* Display.framework */,
|
||||
D0C07F691B67DB4800966E43 /* SwiftSignalKit.framework */,
|
||||
D07516491B2D9E2500AE42E0 /* Postbox.xcconfig */,
|
||||
D0E3A74C1B28A7E300A402D9 /* Postbox */,
|
||||
@@ -174,7 +210,14 @@
|
||||
D0E3A7A11B28B7DC00A402D9 /* Media.swift */,
|
||||
D003E4E51B38DBDB00C22CBC /* MessageView.swift */,
|
||||
D0D225251B4D84930085E26D /* PeerView.swift */,
|
||||
D0977FA11B82930C009994B2 /* PostboxCodingUtils.swift */,
|
||||
D0E3A7811B28ADD000A402D9 /* Postbox.swift */,
|
||||
D0977FA31B829F87009994B2 /* PostboxTables.swift */,
|
||||
D055BD321B7D3D2D00F06C0A /* MediaBox.swift */,
|
||||
D0977F9D1B8234DF009994B2 /* ValueBoxKey.swift */,
|
||||
D0977F9B1B822DB4009994B2 /* ValueBox.swift */,
|
||||
D0977F9F1B8244D7009994B2 /* SqliteValueBox.swift */,
|
||||
D00E0FBD1B85D1B5002E4EB5 /* LmdbValueBox.swift */,
|
||||
D0E3A74D1B28A7E300A402D9 /* Supporting Files */,
|
||||
);
|
||||
path = Postbox;
|
||||
@@ -194,7 +237,6 @@
|
||||
D0E3A7591B28A7E300A402D9 /* PostboxTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D0E3A75C1B28A7E300A402D9 /* PostboxTests.swift */,
|
||||
D0E3A75A1B28A7E300A402D9 /* Supporting Files */,
|
||||
D044E15D1B2ACB9C001EE087 /* CodingTests.swift */,
|
||||
);
|
||||
@@ -216,7 +258,9 @@
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
D00E0FB81B85D192002E4EB5 /* lmdb.h in Headers */,
|
||||
D07516771B2EC90400AE42E0 /* fts3_tokenizer.h in Headers */,
|
||||
D00E0FBB1B85D192002E4EB5 /* midl.h in Headers */,
|
||||
D07516451B2D9CEF00AE42E0 /* sqlite3.h in Headers */,
|
||||
D07516781B2EC90400AE42E0 /* SQLite-Bridging.h in Headers */,
|
||||
D0E3A7501B28A7E300A402D9 /* Postbox.h in Headers */,
|
||||
@@ -327,16 +371,25 @@
|
||||
D0E3A7821B28ADD000A402D9 /* Postbox.swift in Sources */,
|
||||
D0E3A79E1B28B50400A402D9 /* Message.swift in Sources */,
|
||||
D044E1631B2AD677001EE087 /* MurMurHash32.m in Sources */,
|
||||
D00E0FBE1B85D1B5002E4EB5 /* LmdbValueBox.swift in Sources */,
|
||||
D07516721B2EC7FE00AE42E0 /* Value.swift in Sources */,
|
||||
D0977FA41B829F87009994B2 /* PostboxTables.swift in Sources */,
|
||||
D0E3A7A21B28B7DC00A402D9 /* Media.swift in Sources */,
|
||||
D0E3A7881B28AE9C00A402D9 /* Coding.swift in Sources */,
|
||||
D0977F9E1B8234DF009994B2 /* ValueBoxKey.swift in Sources */,
|
||||
D00E0FB91B85D192002E4EB5 /* mdb.c in Sources */,
|
||||
D003E4E61B38DBDB00C22CBC /* MessageView.swift in Sources */,
|
||||
D075165E1B2EC5B500AE42E0 /* module.modulemap in Sources */,
|
||||
D07516791B2EC90400AE42E0 /* SQLite-Bridging.m in Sources */,
|
||||
D0977FA01B8244D7009994B2 /* SqliteValueBox.swift in Sources */,
|
||||
D0D224F21B4D6ABD0085E26D /* Functions.swift in Sources */,
|
||||
D00E0FBA1B85D192002E4EB5 /* midl.c in Sources */,
|
||||
D07516711B2EC7FE00AE42E0 /* Statement.swift in Sources */,
|
||||
D0D225261B4D84930085E26D /* PeerView.swift in Sources */,
|
||||
D0E3A7841B28AE0900A402D9 /* Peer.swift in Sources */,
|
||||
D055BD331B7D3D2D00F06C0A /* MediaBox.swift in Sources */,
|
||||
D0977FA21B82930C009994B2 /* PostboxCodingUtils.swift in Sources */,
|
||||
D0977F9C1B822DB4009994B2 /* ValueBox.swift in Sources */,
|
||||
D0B76BE71B66639F0095CF45 /* DeferredString.swift in Sources */,
|
||||
D075166A1B2EC7FE00AE42E0 /* Database.swift in Sources */,
|
||||
D07516441B2D9CEF00AE42E0 /* sqlite3.c in Sources */,
|
||||
@@ -348,7 +401,6 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
D044E15E1B2ACB9C001EE087 /* CodingTests.swift in Sources */,
|
||||
D0E3A75D1B28A7E300A402D9 /* PostboxTests.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0700"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "D0E3A7541B28A7E300A402D9"
|
||||
BuildableName = "PostboxTests.xctest"
|
||||
BlueprintName = "PostboxTests"
|
||||
ReferencedContainer = "container:Postbox.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -9,6 +9,11 @@
|
||||
<key>orderHint</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
<key>PostboxTests.xcscheme</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>12</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>SuppressBuildableAutocreation</key>
|
||||
<dict>
|
||||
|
||||
@@ -5,7 +5,7 @@ public protocol Coding {
|
||||
func encode(encoder: Encoder)
|
||||
}
|
||||
|
||||
private struct EncodableTypeStore {
|
||||
private final class EncodableTypeStore {
|
||||
var dict: [Int32 : Decoder -> Coding] = [:]
|
||||
|
||||
func decode(typeHash: Int32, decoder: Decoder) -> Coding? {
|
||||
@@ -17,7 +17,10 @@ private struct EncodableTypeStore {
|
||||
}
|
||||
}
|
||||
|
||||
private var typeStore = EncodableTypeStore()
|
||||
private let _typeStore = EncodableTypeStore()
|
||||
private let typeStore = { () -> EncodableTypeStore in
|
||||
return _typeStore
|
||||
}()
|
||||
|
||||
public func declareEncodable(type: Any.Type, f: Decoder -> Coding) {
|
||||
let string = "\(type)"
|
||||
@@ -28,22 +31,62 @@ public func declareEncodable(type: Any.Type, f: Decoder -> Coding) {
|
||||
typeStore.dict[murMurHashString32("\(type)")] = f
|
||||
}
|
||||
|
||||
public final class WriteBuffer {
|
||||
var memory: UnsafeMutablePointer<Void> = nil
|
||||
public class MemoryBuffer: Equatable, CustomStringConvertible {
|
||||
var memory: UnsafeMutablePointer<Void>
|
||||
var capacity: Int
|
||||
var offset: Int
|
||||
var length: Int
|
||||
var freeWhenDone: Bool
|
||||
|
||||
public init(memory: UnsafeMutablePointer<Void>, capacity: Int, length: Int, freeWhenDone: Bool) {
|
||||
self.memory = memory
|
||||
self.capacity = capacity
|
||||
self.length = length
|
||||
self.freeWhenDone = freeWhenDone
|
||||
}
|
||||
|
||||
public init(data: NSData) {
|
||||
self.memory = UnsafeMutablePointer(data.bytes)
|
||||
self.capacity = data.length
|
||||
self.length = data.length
|
||||
self.freeWhenDone = false
|
||||
}
|
||||
|
||||
public init() {
|
||||
self.memory = malloc(32)
|
||||
self.capacity = 32
|
||||
self.offset = 0
|
||||
self.memory = nil
|
||||
self.capacity = 0
|
||||
self.length = 0
|
||||
self.freeWhenDone = false
|
||||
}
|
||||
|
||||
deinit {
|
||||
free(self.memory)
|
||||
if self.freeWhenDone {
|
||||
free(self.memory)
|
||||
}
|
||||
}
|
||||
|
||||
func makeReadBufferAndReset() -> ReadBuffer {
|
||||
public var description: String {
|
||||
let hexString = NSMutableString()
|
||||
let bytes = UnsafeMutablePointer<UInt8>(self.memory)
|
||||
for i in 0 ..< self.length {
|
||||
hexString.appendFormat("%02x", UInt(bytes[i]))
|
||||
}
|
||||
|
||||
return hexString as String
|
||||
}
|
||||
}
|
||||
|
||||
public func ==(lhs: MemoryBuffer, rhs: MemoryBuffer) -> Bool {
|
||||
return lhs.length == rhs.length && memcmp(lhs.memory, rhs.memory, lhs.length) == 0
|
||||
}
|
||||
|
||||
public final class WriteBuffer: MemoryBuffer {
|
||||
var offset = 0
|
||||
|
||||
public override init() {
|
||||
super.init(memory: malloc(32), capacity: 32, length: 0, freeWhenDone: true)
|
||||
}
|
||||
|
||||
public func makeReadBufferAndReset() -> ReadBuffer {
|
||||
let buffer = ReadBuffer(memory: self.memory, length: self.offset, freeWhenDone: true)
|
||||
self.memory = malloc(32)
|
||||
self.capacity = 32
|
||||
@@ -51,7 +94,11 @@ public final class WriteBuffer {
|
||||
return buffer
|
||||
}
|
||||
|
||||
func makeData() -> NSData {
|
||||
public func readBufferNoCopy() -> ReadBuffer {
|
||||
return ReadBuffer(memory: self.memory, length: self.offset, freeWhenDone: false)
|
||||
}
|
||||
|
||||
public func makeData() -> NSData {
|
||||
return NSData(bytes: self.memory, length: self.offset)
|
||||
}
|
||||
|
||||
@@ -59,33 +106,30 @@ public final class WriteBuffer {
|
||||
self.offset = 0
|
||||
}
|
||||
|
||||
func write(data: UnsafePointer<Void>, offset: Int, length: Int) {
|
||||
public func write(data: UnsafePointer<Void>, offset: Int, length: Int) {
|
||||
if self.offset + length > self.capacity {
|
||||
self.capacity = self.offset + length + 256
|
||||
self.memory = realloc(self.memory, self.capacity)
|
||||
}
|
||||
memcpy(self.memory + self.offset, data + offset, length)
|
||||
self.offset += length
|
||||
self.length = self.offset
|
||||
}
|
||||
}
|
||||
|
||||
public final class ReadBuffer {
|
||||
var memory: UnsafeMutablePointer<Void>
|
||||
var length: Int
|
||||
var offset : Int
|
||||
let freeWhenDone: Bool
|
||||
public final class ReadBuffer: MemoryBuffer {
|
||||
var offset = 0
|
||||
|
||||
init(memory: UnsafeMutablePointer<Void>, length: Int, freeWhenDone: Bool) {
|
||||
self.memory = memory
|
||||
self.length = length
|
||||
self.offset = 0
|
||||
self.freeWhenDone = freeWhenDone
|
||||
public init(memory: UnsafeMutablePointer<Void>, length: Int, freeWhenDone: Bool) {
|
||||
super.init(memory: memory, capacity: length, length: length, freeWhenDone: freeWhenDone)
|
||||
}
|
||||
|
||||
deinit {
|
||||
if self.freeWhenDone {
|
||||
free(self.memory)
|
||||
}
|
||||
public init(memoryBufferNoCopy: MemoryBuffer) {
|
||||
super.init(memory: memoryBufferNoCopy.memory, capacity: memoryBufferNoCopy.length, length: memoryBufferNoCopy.length, freeWhenDone: false)
|
||||
}
|
||||
|
||||
func dataNoCopy() -> NSData {
|
||||
return NSData(bytesNoCopy: self.memory, length: self.length, freeWhenDone: false)
|
||||
}
|
||||
|
||||
func read(data: UnsafeMutablePointer<Void>, offset: Int, length: Int) {
|
||||
@@ -118,6 +162,10 @@ public final class Encoder {
|
||||
public init() {
|
||||
}
|
||||
|
||||
public func memoryBuffer() -> MemoryBuffer {
|
||||
return self.buffer
|
||||
}
|
||||
|
||||
public func makeReadBufferAndReset() -> ReadBuffer {
|
||||
return self.buffer.makeReadBufferAndReset()
|
||||
}
|
||||
@@ -130,20 +178,13 @@ public final class Encoder {
|
||||
self.buffer.reset()
|
||||
}
|
||||
|
||||
public func encodeKey(key: UnsafePointer<Int8>) {
|
||||
var length: Int8 = Int8(strlen(key))
|
||||
public func encodeKey(key: StaticString) {
|
||||
var length: Int8 = Int8(key.byteSize)
|
||||
self.buffer.write(&length, offset: 0, length: 1)
|
||||
self.buffer.write(UnsafePointer<Void>(key), offset: 0, length: Int(length))
|
||||
self.buffer.write(key.utf8Start, offset: 0, length: Int(length))
|
||||
}
|
||||
|
||||
public func encodeKey(key: Int8) {
|
||||
var length: Int8 = 1
|
||||
self.buffer.write(&length, offset: 0, length: 1)
|
||||
var keyValue = key
|
||||
self.buffer.write(&keyValue, offset: 0, length: Int(length))
|
||||
}
|
||||
|
||||
public func encodeInt32(value: Int32, forKey key: UnsafePointer<Int8>) {
|
||||
public func encodeInt32(value: Int32, forKey key: StaticString) {
|
||||
self.encodeKey(key)
|
||||
var type: Int8 = ValueType.Int32.rawValue
|
||||
self.buffer.write(&type, offset: 0, length: 1)
|
||||
@@ -151,15 +192,7 @@ public final class Encoder {
|
||||
self.buffer.write(&v, offset: 0, length: 4)
|
||||
}
|
||||
|
||||
public func encodeInt32(value: Int32, forKey key: Int8) {
|
||||
self.encodeKey(key)
|
||||
var type: Int8 = ValueType.Int32.rawValue
|
||||
self.buffer.write(&type, offset: 0, length: 1)
|
||||
var v = value
|
||||
self.buffer.write(&v, offset: 0, length: 4)
|
||||
}
|
||||
|
||||
public func encodeInt64(value: Int64, forKey key: UnsafePointer<Int8>) {
|
||||
public func encodeInt64(value: Int64, forKey key: StaticString) {
|
||||
self.encodeKey(key)
|
||||
var type: Int8 = ValueType.Int64.rawValue
|
||||
self.buffer.write(&type, offset: 0, length: 1)
|
||||
@@ -167,15 +200,7 @@ public final class Encoder {
|
||||
self.buffer.write(&v, offset: 0, length: 8)
|
||||
}
|
||||
|
||||
public func encodeInt64(value: Int64, forKey key: Int8) {
|
||||
self.encodeKey(key)
|
||||
var type: Int8 = ValueType.Int64.rawValue
|
||||
self.buffer.write(&type, offset: 0, length: 1)
|
||||
var v = value
|
||||
self.buffer.write(&v, offset: 0, length: 8)
|
||||
}
|
||||
|
||||
public func encodeBool(value: Bool, forKey key: UnsafePointer<Int8>) {
|
||||
public func encodeBool(value: Bool, forKey key: StaticString) {
|
||||
self.encodeKey(key)
|
||||
var type: Int8 = ValueType.Bool.rawValue
|
||||
self.buffer.write(&type, offset: 0, length: 1)
|
||||
@@ -183,15 +208,7 @@ public final class Encoder {
|
||||
self.buffer.write(&v, offset: 0, length: 1)
|
||||
}
|
||||
|
||||
public func encodeBool(value: Bool, forKey key: Int8) {
|
||||
self.encodeKey(key)
|
||||
var type: Int8 = ValueType.Bool.rawValue
|
||||
self.buffer.write(&type, offset: 0, length: 1)
|
||||
var v: Int8 = value ? 1 : 0
|
||||
self.buffer.write(&v, offset: 0, length: 1)
|
||||
}
|
||||
|
||||
public func encodeDouble(value: Double, forKey key: UnsafePointer<Int8>) {
|
||||
public func encodeDouble(value: Double, forKey key: StaticString) {
|
||||
self.encodeKey(key)
|
||||
var type: Int8 = ValueType.Double.rawValue
|
||||
self.buffer.write(&type, offset: 0, length: 1)
|
||||
@@ -199,15 +216,7 @@ public final class Encoder {
|
||||
self.buffer.write(&v, offset: 0, length: 8)
|
||||
}
|
||||
|
||||
public func encodeDouble(value: Double, forKey key: Int8) {
|
||||
self.encodeKey(key)
|
||||
var type: Int8 = ValueType.Double.rawValue
|
||||
self.buffer.write(&type, offset: 0, length: 1)
|
||||
var v = value
|
||||
self.buffer.write(&v, offset: 0, length: 8)
|
||||
}
|
||||
|
||||
public func encodeString(value: String, forKey key: UnsafePointer<Int8>) {
|
||||
public func encodeString(value: String, forKey key: StaticString) {
|
||||
self.encodeKey(key)
|
||||
var type: Int8 = ValueType.String.rawValue
|
||||
self.buffer.write(&type, offset: 0, length: 1)
|
||||
@@ -217,7 +226,7 @@ public final class Encoder {
|
||||
self.buffer.write(data.bytes, offset: 0, length: Int(length))
|
||||
}
|
||||
|
||||
public func encodeString(value: DeferredString, forKey key: UnsafePointer<Int8>) {
|
||||
public func encodeString(value: DeferredString, forKey key: StaticString) {
|
||||
self.encodeKey(key)
|
||||
var type: Int8 = ValueType.String.rawValue
|
||||
self.buffer.write(&type, offset: 0, length: 1)
|
||||
@@ -227,21 +236,11 @@ public final class Encoder {
|
||||
self.buffer.write(data.bytes, offset: 0, length: Int(length))
|
||||
}
|
||||
|
||||
public func encodeString(value: String, forKey key: Int8) {
|
||||
self.encodeKey(key)
|
||||
var type: Int8 = ValueType.String.rawValue
|
||||
self.buffer.write(&type, offset: 0, length: 1)
|
||||
let data = value.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)!
|
||||
var length: Int32 = Int32(data.length)
|
||||
self.buffer.write(&length, offset: 0, length: 4)
|
||||
self.buffer.write(data.bytes, offset: 0, length: Int(length))
|
||||
}
|
||||
|
||||
public func encodeRootObject(value: Coding) {
|
||||
self.encodeObject(value, forKey: "_")
|
||||
}
|
||||
|
||||
public func encodeObject(value: Coding, forKey key: UnsafePointer<Int8>) {
|
||||
public func encodeObject(value: Coding, forKey key: StaticString) {
|
||||
self.encodeKey(key)
|
||||
var type: Int8 = ValueType.Object.rawValue
|
||||
self.buffer.write(&type, offset: 0, length: 1)
|
||||
@@ -258,7 +257,7 @@ public final class Encoder {
|
||||
self.buffer.write(innerEncoder.buffer.memory, offset: 0, length: Int(length))
|
||||
}
|
||||
|
||||
public func encodeInt32Array(value: [Int32], forKey key: UnsafePointer<Int8>) {
|
||||
public func encodeInt32Array(value: [Int32], forKey key: StaticString) {
|
||||
self.encodeKey(key)
|
||||
var type: Int8 = ValueType.Int32Array.rawValue
|
||||
self.buffer.write(&type, offset: 0, length: 1)
|
||||
@@ -270,7 +269,7 @@ public final class Encoder {
|
||||
}
|
||||
}
|
||||
|
||||
public func encodeInt64Array(value: [Int64], forKey key: UnsafePointer<Int8>) {
|
||||
public func encodeInt64Array(value: [Int64], forKey key: StaticString) {
|
||||
self.encodeKey(key)
|
||||
var type: Int8 = ValueType.Int64Array.rawValue
|
||||
self.buffer.write(&type, offset: 0, length: 1)
|
||||
@@ -282,7 +281,7 @@ public final class Encoder {
|
||||
}
|
||||
}
|
||||
|
||||
public func encodeObjectArray<T: Coding>(value: [T], forKey key: UnsafePointer<Int8>) {
|
||||
public func encodeObjectArray<T: Coding>(value: [T], forKey key: StaticString) {
|
||||
self.encodeKey(key)
|
||||
var type: Int8 = ValueType.ObjectArray.rawValue
|
||||
self.buffer.write(&type, offset: 0, length: 1)
|
||||
@@ -302,7 +301,27 @@ public final class Encoder {
|
||||
}
|
||||
}
|
||||
|
||||
public func encodeObjectDictionary<K, V: Coding where K: Coding, K: Hashable>(value: [K : V], forKey key: UnsafePointer<Int8>) {
|
||||
public func encodeObjectArray(value: [Coding], forKey key: StaticString) {
|
||||
self.encodeKey(key)
|
||||
var type: Int8 = ValueType.ObjectArray.rawValue
|
||||
self.buffer.write(&type, offset: 0, length: 1)
|
||||
var length: Int32 = Int32(value.count)
|
||||
self.buffer.write(&length, offset: 0, length: 4)
|
||||
let innerEncoder = Encoder()
|
||||
for object in value {
|
||||
var typeHash: Int32 = murMurHashString32("\(object.dynamicType)")
|
||||
self.buffer.write(&typeHash, offset: 0, length: 4)
|
||||
|
||||
innerEncoder.reset()
|
||||
object.encode(innerEncoder)
|
||||
|
||||
var length: Int32 = Int32(innerEncoder.buffer.offset)
|
||||
self.buffer.write(&length, offset: 0, length: 4)
|
||||
self.buffer.write(innerEncoder.buffer.memory, offset: 0, length: Int(length))
|
||||
}
|
||||
}
|
||||
|
||||
public func encodeObjectDictionary<K, V: Coding where K: Coding, K: Hashable>(value: [K : V], forKey key: StaticString) {
|
||||
self.encodeKey(key)
|
||||
var type: Int8 = ValueType.ObjectDictionary.rawValue
|
||||
self.buffer.write(&type, offset: 0, length: 1)
|
||||
@@ -329,7 +348,16 @@ public final class Encoder {
|
||||
}
|
||||
}
|
||||
|
||||
public func encodeBytes(bytes: WriteBuffer, forKey key: UnsafePointer<Int8>) {
|
||||
public func encodeBytes(bytes: WriteBuffer, forKey key: StaticString) {
|
||||
self.encodeKey(key)
|
||||
var type: Int8 = ValueType.Bytes.rawValue
|
||||
self.buffer.write(&type, offset: 0, length: 1)
|
||||
var bytesLength: Int32 = Int32(bytes.offset)
|
||||
self.buffer.write(&bytesLength, offset: 0, length: 4)
|
||||
self.buffer.write(bytes.memory, offset: 0, length: bytes.offset)
|
||||
}
|
||||
|
||||
public func encodeBytes(bytes: ReadBuffer, forKey key: StaticString) {
|
||||
self.encodeKey(key)
|
||||
var type: Int8 = ValueType.Bytes.rawValue
|
||||
self.buffer.write(&type, offset: 0, length: 1)
|
||||
@@ -342,9 +370,10 @@ public final class Encoder {
|
||||
}
|
||||
|
||||
public final class Decoder {
|
||||
private let buffer: ReadBuffer
|
||||
private let buffer: MemoryBuffer
|
||||
private var offset: Int = 0
|
||||
|
||||
public init(buffer: ReadBuffer) {
|
||||
public init(buffer: MemoryBuffer) {
|
||||
self.buffer = buffer
|
||||
}
|
||||
|
||||
@@ -407,11 +436,11 @@ public final class Decoder {
|
||||
}
|
||||
}
|
||||
|
||||
private class func positionOnKey(bytes: UnsafePointer<Int8>, inout offset: Int, maxOffset: Int, length: Int, key: UnsafePointer<Int8>, valueType: ValueType) -> Bool
|
||||
private class func positionOnKey(bytes: UnsafePointer<Int8>, inout offset: Int, maxOffset: Int, length: Int, key: StaticString, valueType: ValueType) -> Bool
|
||||
{
|
||||
let startOffset = offset
|
||||
|
||||
let keyLength: Int = Int(strlen(key))
|
||||
let keyLength: Int = key.byteSize
|
||||
while (offset < maxOffset)
|
||||
{
|
||||
let readKeyLength = bytes[offset]
|
||||
@@ -421,7 +450,7 @@ public final class Decoder {
|
||||
let readValueType = bytes[offset]
|
||||
offset += 1
|
||||
|
||||
if readValueType != valueType.rawValue || keyLength != Int(readKeyLength) || memcmp(bytes + (offset - Int(readKeyLength) - 1), key, keyLength) != 0 {
|
||||
if readValueType != valueType.rawValue || keyLength != Int(readKeyLength) || memcmp(bytes + (offset - Int(readKeyLength) - 1), key.utf8Start, keyLength) != 0 {
|
||||
skipValue(bytes, offset: &offset, length: length, valueType: ValueType(rawValue: readValueType)!)
|
||||
} else {
|
||||
return true
|
||||
@@ -468,67 +497,78 @@ public final class Decoder {
|
||||
return false
|
||||
}
|
||||
|
||||
public func decodeInt32ForKey(key: UnsafePointer<Int8>) -> Int32 {
|
||||
if Decoder.positionOnKey(UnsafePointer<Int8>(self.buffer.memory), offset: &self.buffer.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Int32) {
|
||||
public func decodeInt32ForKey(key: StaticString) -> Int32 {
|
||||
if Decoder.positionOnKey(UnsafePointer<Int8>(self.buffer.memory), offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Int32) {
|
||||
var value: Int32 = 0
|
||||
memcpy(&value, self.buffer.memory + self.buffer.offset, 4)
|
||||
self.buffer.offset += 4
|
||||
memcpy(&value, self.buffer.memory + self.offset, 4)
|
||||
self.offset += 4
|
||||
return value
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
public func decodeInt32ForKey(key: UnsafePointer<Int8>) -> Int32? {
|
||||
if Decoder.positionOnKey(UnsafePointer<Int8>(self.buffer.memory), offset: &self.buffer.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Int32) {
|
||||
public func decodeInt32ForKey(key: StaticString) -> Int32? {
|
||||
if Decoder.positionOnKey(UnsafePointer<Int8>(self.buffer.memory), offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Int32) {
|
||||
var value: Int32 = 0
|
||||
memcpy(&value, self.buffer.memory + self.buffer.offset, 4)
|
||||
self.buffer.offset += 4
|
||||
memcpy(&value, self.buffer.memory + self.offset, 4)
|
||||
self.offset += 4
|
||||
return value
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
public func decodeInt64ForKey(key: UnsafePointer<Int8>) -> Int64 {
|
||||
if Decoder.positionOnKey(UnsafePointer<Int8>(self.buffer.memory), offset: &self.buffer.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Int64) {
|
||||
public func decodeInt64ForKey(key: StaticString) -> Int64 {
|
||||
if Decoder.positionOnKey(UnsafePointer<Int8>(self.buffer.memory), offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Int64) {
|
||||
var value: Int64 = 0
|
||||
memcpy(&value, self.buffer.memory + self.buffer.offset, 8)
|
||||
self.buffer.offset += 8
|
||||
memcpy(&value, self.buffer.memory + self.offset, 8)
|
||||
self.offset += 8
|
||||
return value
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
public func decodeBoolForKey(key: UnsafePointer<Int8>) -> Bool {
|
||||
if Decoder.positionOnKey(UnsafePointer<Int8>(self.buffer.memory), offset: &self.buffer.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Bool) {
|
||||
public func decodeInt64ForKey(key: StaticString) -> Int64? {
|
||||
if Decoder.positionOnKey(UnsafePointer<Int8>(self.buffer.memory), offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Int64) {
|
||||
var value: Int64 = 0
|
||||
memcpy(&value, self.buffer.memory + self.offset, 8)
|
||||
self.offset += 8
|
||||
return value
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
public func decodeBoolForKey(key: StaticString) -> Bool {
|
||||
if Decoder.positionOnKey(UnsafePointer<Int8>(self.buffer.memory), offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Bool) {
|
||||
var value: Int8 = 0
|
||||
memcpy(&value, self.buffer.memory + self.buffer.offset, 1)
|
||||
self.buffer.offset += 1
|
||||
memcpy(&value, self.buffer.memory + self.offset, 1)
|
||||
self.offset += 1
|
||||
return value != 0
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
public func decodeDoubleForKey(key: UnsafePointer<Int8>) -> Double {
|
||||
if Decoder.positionOnKey(UnsafePointer<Int8>(self.buffer.memory), offset: &self.buffer.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Double) {
|
||||
public func decodeDoubleForKey(key: StaticString) -> Double {
|
||||
if Decoder.positionOnKey(UnsafePointer<Int8>(self.buffer.memory), offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Double) {
|
||||
var value: Double = 0
|
||||
memcpy(&value, self.buffer.memory + self.buffer.offset, 8)
|
||||
self.buffer.offset += 8
|
||||
memcpy(&value, self.buffer.memory + self.offset, 8)
|
||||
self.offset += 8
|
||||
return value
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
public func decodeStringForKey(key: UnsafePointer<Int8>) -> String {
|
||||
if Decoder.positionOnKey(UnsafePointer<Int8>(self.buffer.memory), offset: &self.buffer.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .String) {
|
||||
public func decodeStringForKey(key: StaticString) -> String {
|
||||
if Decoder.positionOnKey(UnsafePointer<Int8>(self.buffer.memory), offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .String) {
|
||||
var length: Int32 = 0
|
||||
memcpy(&length, self.buffer.memory + self.buffer.offset, 4)
|
||||
let data = NSData(bytes: self.buffer.memory + (self.buffer.offset + 4), length: Int(length))
|
||||
self.buffer.offset += 4 + Int(length)
|
||||
memcpy(&length, self.buffer.memory + self.offset, 4)
|
||||
let data = NSData(bytes: self.buffer.memory + (self.offset + 4), length: Int(length))
|
||||
self.offset += 4 + Int(length)
|
||||
let value = NSString(data: data, encoding: NSUTF8StringEncoding)
|
||||
return (value as? String) ?? ""
|
||||
} else {
|
||||
@@ -536,12 +576,12 @@ public final class Decoder {
|
||||
}
|
||||
}
|
||||
|
||||
public func decodeStringForKey(key: UnsafePointer<Int8>) -> String? {
|
||||
if Decoder.positionOnKey(UnsafePointer<Int8>(self.buffer.memory), offset: &self.buffer.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .String) {
|
||||
public func decodeStringForKey(key: StaticString) -> String? {
|
||||
if Decoder.positionOnKey(UnsafePointer<Int8>(self.buffer.memory), offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .String) {
|
||||
var length: Int32 = 0
|
||||
memcpy(&length, self.buffer.memory + self.buffer.offset, 4)
|
||||
let data = NSData(bytes: self.buffer.memory + (self.buffer.offset + 4), length: Int(length))
|
||||
self.buffer.offset += 4 + Int(length)
|
||||
memcpy(&length, self.buffer.memory + self.offset, 4)
|
||||
let data = NSData(bytes: self.buffer.memory + (self.offset + 4), length: Int(length))
|
||||
self.offset += 4 + Int(length)
|
||||
let value = NSString(data: data, encoding: NSUTF8StringEncoding)
|
||||
return value as? String
|
||||
} else {
|
||||
@@ -549,24 +589,24 @@ public final class Decoder {
|
||||
}
|
||||
}
|
||||
|
||||
public func decodeStringForKey(key: UnsafePointer<Int8>) -> DeferredString {
|
||||
if Decoder.positionOnKey(UnsafePointer<Int8>(self.buffer.memory), offset: &self.buffer.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .String) {
|
||||
public func decodeStringForKey(key: StaticString) -> DeferredString {
|
||||
if Decoder.positionOnKey(UnsafePointer<Int8>(self.buffer.memory), offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .String) {
|
||||
var length: Int32 = 0
|
||||
memcpy(&length, self.buffer.memory + self.buffer.offset, 4)
|
||||
let data = NSData(bytes: self.buffer.memory + (self.buffer.offset + 4), length: Int(length))
|
||||
self.buffer.offset += 4 + Int(length)
|
||||
memcpy(&length, self.buffer.memory + self.offset, 4)
|
||||
let data = NSData(bytes: self.buffer.memory + (self.offset + 4), length: Int(length))
|
||||
self.offset += 4 + Int(length)
|
||||
return DeferredStringValue(data)
|
||||
} else {
|
||||
return DeferredStringValue("")
|
||||
}
|
||||
}
|
||||
|
||||
public func decodeStringForKey(key: UnsafePointer<Int8>) -> DeferredString? {
|
||||
if Decoder.positionOnKey(UnsafePointer<Int8>(self.buffer.memory), offset: &self.buffer.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .String) {
|
||||
public func decodeStringForKey(key: StaticString) -> DeferredString? {
|
||||
if Decoder.positionOnKey(UnsafePointer<Int8>(self.buffer.memory), offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .String) {
|
||||
var length: Int32 = 0
|
||||
memcpy(&length, self.buffer.memory + self.buffer.offset, 4)
|
||||
let data = NSData(bytes: self.buffer.memory + (self.buffer.offset + 4), length: Int(length))
|
||||
self.buffer.offset += 4 + Int(length)
|
||||
memcpy(&length, self.buffer.memory + self.offset, 4)
|
||||
let data = NSData(bytes: self.buffer.memory + (self.offset + 4), length: Int(length))
|
||||
self.offset += 4 + Int(length)
|
||||
return DeferredStringValue(data)
|
||||
} else {
|
||||
return nil
|
||||
@@ -577,17 +617,17 @@ public final class Decoder {
|
||||
return self.decodeObjectForKey("_")
|
||||
}
|
||||
|
||||
public func decodeObjectForKey(key: UnsafePointer<Int8>) -> Coding? {
|
||||
if Decoder.positionOnKey(UnsafePointer<Int8>(self.buffer.memory), offset: &self.buffer.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Object) {
|
||||
public func decodeObjectForKey(key: StaticString) -> Coding? {
|
||||
if Decoder.positionOnKey(UnsafePointer<Int8>(self.buffer.memory), offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Object) {
|
||||
var typeHash: Int32 = 0
|
||||
memcpy(&typeHash, self.buffer.memory + self.buffer.offset, 4)
|
||||
self.buffer.offset += 4
|
||||
memcpy(&typeHash, self.buffer.memory + self.offset, 4)
|
||||
self.offset += 4
|
||||
|
||||
var length: Int32 = 0
|
||||
memcpy(&length, self.buffer.memory + self.buffer.offset, 4)
|
||||
memcpy(&length, self.buffer.memory + self.offset, 4)
|
||||
|
||||
let innerDecoder = Decoder(buffer: ReadBuffer(memory: self.buffer.memory + (self.buffer.offset + 4), length: Int(length), freeWhenDone: false))
|
||||
self.buffer.offset += 4 + Int(length)
|
||||
let innerDecoder = Decoder(buffer: ReadBuffer(memory: self.buffer.memory + (self.offset + 4), length: Int(length), freeWhenDone: false))
|
||||
self.offset += 4 + Int(length)
|
||||
|
||||
return typeStore.decode(typeHash, decoder: innerDecoder)
|
||||
} else {
|
||||
@@ -595,17 +635,17 @@ public final class Decoder {
|
||||
}
|
||||
}
|
||||
|
||||
public func decodeObjectForKey(key: UnsafePointer<Int8>, decoder: Decoder -> Coding) -> Coding? {
|
||||
if Decoder.positionOnKey(UnsafePointer<Int8>(self.buffer.memory), offset: &self.buffer.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Object) {
|
||||
public func decodeObjectForKey(key: StaticString, decoder: Decoder -> Coding) -> Coding? {
|
||||
if Decoder.positionOnKey(UnsafePointer<Int8>(self.buffer.memory), offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Object) {
|
||||
var typeHash: Int32 = 0
|
||||
memcpy(&typeHash, self.buffer.memory + self.buffer.offset, 4)
|
||||
self.buffer.offset += 4
|
||||
memcpy(&typeHash, self.buffer.memory + self.offset, 4)
|
||||
self.offset += 4
|
||||
|
||||
var length: Int32 = 0
|
||||
memcpy(&length, self.buffer.memory + self.buffer.offset, 4)
|
||||
memcpy(&length, self.buffer.memory + self.offset, 4)
|
||||
|
||||
let innerDecoder = Decoder(buffer: ReadBuffer(memory: self.buffer.memory + (self.buffer.offset + 4), length: Int(length), freeWhenDone: false))
|
||||
self.buffer.offset += 4 + Int(length)
|
||||
let innerDecoder = Decoder(buffer: ReadBuffer(memory: self.buffer.memory + (self.offset + 4), length: Int(length), freeWhenDone: false))
|
||||
self.offset += 4 + Int(length)
|
||||
|
||||
return decoder(innerDecoder)
|
||||
} else {
|
||||
@@ -613,51 +653,51 @@ public final class Decoder {
|
||||
}
|
||||
}
|
||||
|
||||
public func decodeInt32ArrayForKey(key: UnsafePointer<Int8>) -> [Int32] {
|
||||
if Decoder.positionOnKey(UnsafePointer<Int8>(self.buffer.memory), offset: &self.buffer.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Int32Array) {
|
||||
public func decodeInt32ArrayForKey(key: StaticString) -> [Int32] {
|
||||
if Decoder.positionOnKey(UnsafePointer<Int8>(self.buffer.memory), offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Int32Array) {
|
||||
var length: Int32 = 0
|
||||
memcpy(&length, self.buffer.memory + self.buffer.offset, 4)
|
||||
memcpy(&length, self.buffer.memory + self.offset, 4)
|
||||
var array: [Int32] = []
|
||||
array.reserveCapacity(Int(length))
|
||||
var i: Int32 = 0
|
||||
while i < length {
|
||||
var element: Int32 = 0
|
||||
memcpy(&element, self.buffer.memory + (self.buffer.offset + 4 + 4 * Int(i)), 4)
|
||||
memcpy(&element, self.buffer.memory + (self.offset + 4 + 4 * Int(i)), 4)
|
||||
array.append(element)
|
||||
i++
|
||||
}
|
||||
self.buffer.offset += 4 + Int(length) * 4
|
||||
self.offset += 4 + Int(length) * 4
|
||||
return array
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
public func decodeInt64ArrayForKey(key: UnsafePointer<Int8>) -> [Int64] {
|
||||
if Decoder.positionOnKey(UnsafePointer<Int8>(self.buffer.memory), offset: &self.buffer.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Int64Array) {
|
||||
public func decodeInt64ArrayForKey(key: StaticString) -> [Int64] {
|
||||
if Decoder.positionOnKey(UnsafePointer<Int8>(self.buffer.memory), offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Int64Array) {
|
||||
var length: Int32 = 0
|
||||
memcpy(&length, self.buffer.memory + self.buffer.offset, 4)
|
||||
memcpy(&length, self.buffer.memory + self.offset, 4)
|
||||
var array: [Int64] = []
|
||||
array.reserveCapacity(Int(length))
|
||||
var i: Int32 = 0
|
||||
while i < length {
|
||||
var element: Int64 = 0
|
||||
memcpy(&element, self.buffer.memory + (self.buffer.offset + 4 + 8 * Int(i)), 8)
|
||||
memcpy(&element, self.buffer.memory + (self.offset + 4 + 8 * Int(i)), 8)
|
||||
array.append(element)
|
||||
i++
|
||||
}
|
||||
self.buffer.offset += 4 + Int(length) * 8
|
||||
self.offset += 4 + Int(length) * 8
|
||||
return array
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
public func decodeObjectArrayForKey<T: Coding>(key: UnsafePointer<Int8>) -> [T] {
|
||||
if Decoder.positionOnKey(UnsafePointer<Int8>(self.buffer.memory), offset: &self.buffer.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .ObjectArray) {
|
||||
public func decodeObjectArrayForKey<T where T: Coding>(key: StaticString) -> [T] {
|
||||
if Decoder.positionOnKey(UnsafePointer<Int8>(self.buffer.memory), offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .ObjectArray) {
|
||||
var length: Int32 = 0
|
||||
memcpy(&length, self.buffer.memory + self.buffer.offset, 4)
|
||||
self.buffer.offset += 4
|
||||
memcpy(&length, self.buffer.memory + self.offset, 4)
|
||||
self.offset += 4
|
||||
|
||||
var array: [T] = []
|
||||
array.reserveCapacity(Int(length))
|
||||
@@ -666,14 +706,14 @@ public final class Decoder {
|
||||
var i: Int32 = 0
|
||||
while i < length {
|
||||
var typeHash: Int32 = 0
|
||||
memcpy(&typeHash, self.buffer.memory + self.buffer.offset, 4)
|
||||
self.buffer.offset += 4
|
||||
memcpy(&typeHash, self.buffer.memory + self.offset, 4)
|
||||
self.offset += 4
|
||||
|
||||
var objectLength: Int32 = 0
|
||||
memcpy(&objectLength, self.buffer.memory + self.buffer.offset, 4)
|
||||
memcpy(&objectLength, self.buffer.memory + self.offset, 4)
|
||||
|
||||
let innerDecoder = Decoder(buffer: ReadBuffer(memory: self.buffer.memory + (self.buffer.offset + 4), length: Int(objectLength), freeWhenDone: false))
|
||||
self.buffer.offset += 4 + Int(objectLength)
|
||||
let innerDecoder = Decoder(buffer: ReadBuffer(memory: self.buffer.memory + (self.offset + 4), length: Int(objectLength), freeWhenDone: false))
|
||||
self.offset += 4 + Int(objectLength)
|
||||
|
||||
if !failed {
|
||||
if let object = typeStore.decode(typeHash, decoder: innerDecoder) as? T {
|
||||
@@ -696,11 +736,54 @@ public final class Decoder {
|
||||
}
|
||||
}
|
||||
|
||||
public func decodeObjectDictionaryForKey<K, V: Coding where K: Coding, K: Hashable>(key: UnsafePointer<Int8>) -> [K : V] {
|
||||
if Decoder.positionOnKey(UnsafePointer<Int8>(self.buffer.memory), offset: &self.buffer.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .ObjectDictionary) {
|
||||
public func decodeObjectArrayForKey(key: StaticString) -> [Coding] {
|
||||
if Decoder.positionOnKey(UnsafePointer<Int8>(self.buffer.memory), offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .ObjectArray) {
|
||||
var length: Int32 = 0
|
||||
memcpy(&length, self.buffer.memory + self.buffer.offset, 4)
|
||||
self.buffer.offset += 4
|
||||
memcpy(&length, self.buffer.memory + self.offset, 4)
|
||||
self.offset += 4
|
||||
|
||||
var array: [Coding] = []
|
||||
array.reserveCapacity(Int(length))
|
||||
|
||||
var failed = false
|
||||
var i: Int32 = 0
|
||||
while i < length {
|
||||
var typeHash: Int32 = 0
|
||||
memcpy(&typeHash, self.buffer.memory + self.offset, 4)
|
||||
self.offset += 4
|
||||
|
||||
var objectLength: Int32 = 0
|
||||
memcpy(&objectLength, self.buffer.memory + self.offset, 4)
|
||||
|
||||
let innerDecoder = Decoder(buffer: ReadBuffer(memory: self.buffer.memory + (self.offset + 4), length: Int(objectLength), freeWhenDone: false))
|
||||
self.offset += 4 + Int(objectLength)
|
||||
|
||||
if !failed {
|
||||
if let object = typeStore.decode(typeHash, decoder: innerDecoder) {
|
||||
array.append(object)
|
||||
} else {
|
||||
failed = true
|
||||
}
|
||||
}
|
||||
|
||||
i++
|
||||
}
|
||||
|
||||
if failed {
|
||||
return []
|
||||
} else {
|
||||
return array
|
||||
}
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
public func decodeObjectDictionaryForKey<K, V: Coding where K: Coding, K: Hashable>(key: StaticString) -> [K : V] {
|
||||
if Decoder.positionOnKey(UnsafePointer<Int8>(self.buffer.memory), offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .ObjectDictionary) {
|
||||
var length: Int32 = 0
|
||||
memcpy(&length, self.buffer.memory + self.offset, 4)
|
||||
self.offset += 4
|
||||
|
||||
var dictionary: [K : V] = [:]
|
||||
|
||||
@@ -708,26 +791,26 @@ public final class Decoder {
|
||||
var i: Int32 = 0
|
||||
while i < length {
|
||||
var keyHash: Int32 = 0
|
||||
memcpy(&keyHash, self.buffer.memory + self.buffer.offset, 4)
|
||||
self.buffer.offset += 4
|
||||
memcpy(&keyHash, self.buffer.memory + self.offset, 4)
|
||||
self.offset += 4
|
||||
|
||||
var keyLength: Int32 = 0
|
||||
memcpy(&keyLength, self.buffer.memory + self.buffer.offset, 4)
|
||||
memcpy(&keyLength, self.buffer.memory + self.offset, 4)
|
||||
|
||||
var innerDecoder = Decoder(buffer: ReadBuffer(memory: self.buffer.memory + (self.buffer.offset + 4), length: Int(keyLength), freeWhenDone: false))
|
||||
self.buffer.offset += 4 + Int(keyLength)
|
||||
var innerDecoder = Decoder(buffer: ReadBuffer(memory: self.buffer.memory + (self.offset + 4), length: Int(keyLength), freeWhenDone: false))
|
||||
self.offset += 4 + Int(keyLength)
|
||||
|
||||
let key = failed ? nil : (typeStore.decode(keyHash, decoder: innerDecoder) as? K)
|
||||
|
||||
var valueHash: Int32 = 0
|
||||
memcpy(&valueHash, self.buffer.memory + self.buffer.offset, 4)
|
||||
self.buffer.offset += 4
|
||||
memcpy(&valueHash, self.buffer.memory + self.offset, 4)
|
||||
self.offset += 4
|
||||
|
||||
var valueLength: Int32 = 0
|
||||
memcpy(&valueLength, self.buffer.memory + self.buffer.offset, 4)
|
||||
memcpy(&valueLength, self.buffer.memory + self.offset, 4)
|
||||
|
||||
innerDecoder = Decoder(buffer: ReadBuffer(memory: self.buffer.memory + (self.buffer.offset + 4), length: Int(valueLength), freeWhenDone: false))
|
||||
self.buffer.offset += 4 + Int(valueLength)
|
||||
innerDecoder = Decoder(buffer: ReadBuffer(memory: self.buffer.memory + (self.offset + 4), length: Int(valueLength), freeWhenDone: false))
|
||||
self.offset += 4 + Int(valueLength)
|
||||
|
||||
let value = failed ? nil : (typeStore.decode(valueHash, decoder: innerDecoder) as? V)
|
||||
|
||||
@@ -750,12 +833,12 @@ public final class Decoder {
|
||||
}
|
||||
}
|
||||
|
||||
public func decodeBytesForKeyNoCopy(key: UnsafePointer<Int8>) -> ReadBuffer! {
|
||||
if Decoder.positionOnKey(UnsafePointer<Int8>(self.buffer.memory), offset: &self.buffer.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Bytes) {
|
||||
public func decodeBytesForKeyNoCopy(key: StaticString) -> ReadBuffer! {
|
||||
if Decoder.positionOnKey(UnsafePointer<Int8>(self.buffer.memory), offset: &self.offset, maxOffset: self.buffer.length, length: self.buffer.length, key: key, valueType: .Bytes) {
|
||||
var length: Int32 = 0
|
||||
memcpy(&length, self.buffer.memory + self.buffer.offset, 4)
|
||||
self.buffer.offset += 4 + Int(length)
|
||||
return ReadBuffer(memory: UnsafeMutablePointer<Int8>(self.buffer.memory + (self.buffer.offset - Int(length))), length: Int(length), freeWhenDone: false)
|
||||
memcpy(&length, self.buffer.memory + self.offset, 4)
|
||||
self.offset += 4 + Int(length)
|
||||
return ReadBuffer(memory: UnsafeMutablePointer<Int8>(self.buffer.memory + (self.offset - Int(length))), length: Int(length), freeWhenDone: false)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
||||
395
Postbox/LmdbValueBox.swift
Normal file
395
Postbox/LmdbValueBox.swift
Normal file
@@ -0,0 +1,395 @@
|
||||
import Foundation
|
||||
import sqlcipher
|
||||
|
||||
private struct LmdbTable {
|
||||
var dbi: MDB_dbi
|
||||
}
|
||||
|
||||
private struct LmdbCursor {
|
||||
var cursor: COpaquePointer
|
||||
|
||||
func seekTo(key: ValueBoxKey, forward: Bool) -> (ValueBoxKey, ReadBuffer)? {
|
||||
var mdbKey = MDB_val()
|
||||
var mdbData = MDB_val()
|
||||
|
||||
mdbKey.mv_data = key.memory
|
||||
mdbKey.mv_size = key.length
|
||||
mdbData.mv_data = nil
|
||||
mdbData.mv_size = 0
|
||||
|
||||
let result = mdb_cursor_get(self.cursor, &mdbKey, &mdbData, forward ? MDB_SET_RANGE : MDB_SET_KEY)
|
||||
|
||||
if result == MDB_SUCCESS {
|
||||
let actualKey = ValueBoxKey(length: mdbKey.mv_size)
|
||||
memcpy(actualKey.memory, mdbKey.mv_data, mdbKey.mv_size)
|
||||
|
||||
let value = malloc(mdbData.mv_size)
|
||||
memcpy(value, mdbData.mv_data, mdbData.mv_size)
|
||||
|
||||
return (actualKey, ReadBuffer(memory: value, length: mdbData.mv_size, freeWhenDone: true))
|
||||
} else if result == MDB_NOTFOUND {
|
||||
if !forward {
|
||||
return self.previous()
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
print("(LmdbValueBox mdb_cursor_get failed with \(result))")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func previous() -> (ValueBoxKey, ReadBuffer)? {
|
||||
var mdbKey = MDB_val()
|
||||
var mdbData = MDB_val()
|
||||
|
||||
mdbKey.mv_data = nil
|
||||
mdbKey.mv_size = 0
|
||||
mdbData.mv_data = nil
|
||||
mdbData.mv_size = 0
|
||||
|
||||
let result = mdb_cursor_get(self.cursor, &mdbKey, &mdbData, MDB_PREV)
|
||||
if result == MDB_SUCCESS {
|
||||
let actualKey = ValueBoxKey(length: mdbKey.mv_size)
|
||||
memcpy(actualKey.memory, mdbKey.mv_data, mdbKey.mv_size)
|
||||
|
||||
let value = malloc(mdbData.mv_size)
|
||||
memcpy(value, mdbData.mv_data, mdbData.mv_size)
|
||||
|
||||
return (actualKey, ReadBuffer(memory: value, length: mdbData.mv_size, freeWhenDone: true))
|
||||
} else if result == MDB_NOTFOUND {
|
||||
return nil
|
||||
} else {
|
||||
print("(LmdbValueBox mdb_cursor_get failed with \(result))")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func next() -> (ValueBoxKey, ReadBuffer)? {
|
||||
var mdbKey = MDB_val()
|
||||
var mdbData = MDB_val()
|
||||
|
||||
mdbKey.mv_data = nil
|
||||
mdbKey.mv_size = 0
|
||||
mdbData.mv_data = nil
|
||||
mdbData.mv_size = 0
|
||||
|
||||
let result = mdb_cursor_get(self.cursor, &mdbKey, &mdbData, MDB_NEXT)
|
||||
if result == MDB_SUCCESS {
|
||||
let actualKey = ValueBoxKey(length: mdbKey.mv_size)
|
||||
memcpy(actualKey.memory, mdbKey.mv_data, mdbKey.mv_size)
|
||||
|
||||
let value = malloc(mdbData.mv_size)
|
||||
memcpy(value, mdbData.mv_data, mdbData.mv_size)
|
||||
|
||||
return (actualKey, ReadBuffer(memory: value, length: mdbData.mv_size, freeWhenDone: true))
|
||||
} else if result == MDB_NOTFOUND {
|
||||
return nil
|
||||
} else {
|
||||
print("(LmdbValueBox mdb_cursor_get failed with \(result))")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final class LmdbValueBox: ValueBox {
|
||||
private var env: COpaquePointer = nil
|
||||
private var tables: [Int32 : LmdbTable] = [:]
|
||||
|
||||
private var sharedTxn: COpaquePointer = nil
|
||||
|
||||
public init?(basePath: String) {
|
||||
var result = mdb_env_create(&self.env)
|
||||
if result != MDB_SUCCESS {
|
||||
print("(LmdbValueBox mdb_env_create failed with \(result))")
|
||||
return nil
|
||||
}
|
||||
|
||||
let path = basePath + "/lmdb"
|
||||
|
||||
var createDirectory = false
|
||||
var isDirectory: ObjCBool = false as ObjCBool
|
||||
if NSFileManager.defaultManager().fileExistsAtPath(path, isDirectory: &isDirectory) {
|
||||
if !isDirectory {
|
||||
do {
|
||||
try NSFileManager.defaultManager().removeItemAtPath(path)
|
||||
} catch _ { }
|
||||
createDirectory = true
|
||||
}
|
||||
}
|
||||
else {
|
||||
createDirectory = true
|
||||
}
|
||||
|
||||
if createDirectory {
|
||||
do {
|
||||
try NSFileManager.defaultManager().createDirectoryAtPath(path, withIntermediateDirectories: true, attributes: nil)
|
||||
} catch _ { }
|
||||
}
|
||||
|
||||
mdb_env_set_mapsize(self.env, 500 * 1024 * 1024);
|
||||
mdb_env_set_maxdbs(self.env, 64)
|
||||
|
||||
path.withCString { string in
|
||||
result = mdb_env_open(self.env, string, UInt32(MDB_NOSYNC), 0o664)
|
||||
}
|
||||
if result != MDB_SUCCESS {
|
||||
print("(LmdbValueBox mdb_env_open failed with \(result))")
|
||||
return nil
|
||||
}
|
||||
|
||||
var removedReaders: Int32 = 0
|
||||
result = mdb_reader_check(self.env, &removedReaders)
|
||||
|
||||
if removedReaders != 0 {
|
||||
print("(LmdbValueBox removed \(removedReaders) stale readers)")
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
mdb_env_close(self.env)
|
||||
}
|
||||
|
||||
private func createTableWithName(name: Int32) -> LmdbTable? {
|
||||
var dbi = MDB_dbi()
|
||||
let result = mdb_dbi_open(self.sharedTxn, "\(name)", UInt32(MDB_CREATE), &dbi)
|
||||
|
||||
if result != MDB_SUCCESS {
|
||||
print("(LmdbValueBox mdb_dbi_open failed with \(result))")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return LmdbTable(dbi: dbi)
|
||||
}
|
||||
|
||||
public func begin() {
|
||||
if self.sharedTxn != nil {
|
||||
print("(LmdbValueBox already in transaction)")
|
||||
} else {
|
||||
let result = mdb_txn_begin(self.env, nil, 0, &sharedTxn)
|
||||
if result != MDB_SUCCESS {
|
||||
print("(LmdbValueBox txn_begin failed with \(result))")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func commit() {
|
||||
if self.sharedTxn == nil {
|
||||
print("(LmdbValueBox already no current transaction)")
|
||||
} else {
|
||||
let result = mdb_txn_commit(self.sharedTxn)
|
||||
self.sharedTxn = nil
|
||||
if result != MDB_SUCCESS {
|
||||
print("(LmdbValueBox txn_commit failed with \(result))")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func range(table: Int32, start: ValueBoxKey, end: ValueBoxKey, @noescape values: (ValueBoxKey, ReadBuffer) -> Bool, limit: Int) {
|
||||
if start == end || limit == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
var commit = false
|
||||
if self.sharedTxn == nil {
|
||||
self.begin()
|
||||
commit = true
|
||||
}
|
||||
|
||||
var nativeTable: LmdbTable?
|
||||
if let existingTable = self.tables[table] {
|
||||
nativeTable = existingTable
|
||||
} else if let createdTable = self.createTableWithName(table) {
|
||||
nativeTable = createdTable
|
||||
self.tables[table] = createdTable
|
||||
}
|
||||
|
||||
if let nativeTable = nativeTable {
|
||||
var cursorPtr: COpaquePointer = nil
|
||||
let result = mdb_cursor_open(self.sharedTxn, nativeTable.dbi, &cursorPtr)
|
||||
if result != MDB_SUCCESS {
|
||||
print("(LmdbValueBox mdb_cursor_open failed with \(result))")
|
||||
} else {
|
||||
let cursor = LmdbCursor(cursor: cursorPtr)
|
||||
|
||||
if start < end {
|
||||
var value = cursor.seekTo(start, forward: true)
|
||||
if value != nil {
|
||||
if value!.0 == start {
|
||||
value = cursor.next()
|
||||
}
|
||||
}
|
||||
|
||||
var count = 0
|
||||
if value != nil && value!.0 < end {
|
||||
count++
|
||||
values(value!.0, value!.1)
|
||||
}
|
||||
|
||||
while value != nil && value!.0 < end && count < limit {
|
||||
value = cursor.next()
|
||||
if value != nil && value!.0 < end {
|
||||
count++
|
||||
values(value!.0, value!.1)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var value = cursor.seekTo(start, forward: false)
|
||||
if value != nil {
|
||||
if value!.0 == start {
|
||||
value = cursor.previous()
|
||||
}
|
||||
}
|
||||
|
||||
var count = 0
|
||||
if value != nil && value!.0 > end {
|
||||
count++
|
||||
values(value!.0, value!.1)
|
||||
}
|
||||
|
||||
while value != nil && value!.0 > end && count < limit {
|
||||
value = cursor.previous()
|
||||
if value != nil && value!.0 > end {
|
||||
count++
|
||||
values(value!.0, value!.1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mdb_cursor_close(cursorPtr)
|
||||
}
|
||||
}
|
||||
|
||||
if commit {
|
||||
self.commit()
|
||||
}
|
||||
}
|
||||
|
||||
public func range(table: Int32, start: ValueBoxKey, end: ValueBoxKey, keys: ValueBoxKey -> Bool, limit: Int) {
|
||||
self.range(table, start: start, end: end, values: { key, _ in
|
||||
return keys(key)
|
||||
}, limit: limit)
|
||||
}
|
||||
|
||||
public func get(table: Int32, key: ValueBoxKey) -> ReadBuffer? {
|
||||
var commit = false
|
||||
if self.sharedTxn == nil {
|
||||
self.begin()
|
||||
commit = true
|
||||
}
|
||||
|
||||
var nativeTable: LmdbTable?
|
||||
if let existingTable = self.tables[table] {
|
||||
nativeTable = existingTable
|
||||
} else if let createdTable = self.createTableWithName(table) {
|
||||
nativeTable = createdTable
|
||||
self.tables[table] = createdTable
|
||||
}
|
||||
|
||||
var resultValue: ReadBuffer?
|
||||
|
||||
if let nativeTable = nativeTable {
|
||||
var mdbKey = MDB_val()
|
||||
var mdbData = MDB_val()
|
||||
|
||||
mdbKey.mv_data = key.memory
|
||||
mdbKey.mv_size = key.length
|
||||
|
||||
let result = mdb_get(self.sharedTxn, nativeTable.dbi, &mdbKey, &mdbData)
|
||||
|
||||
if result == MDB_SUCCESS {
|
||||
let value = malloc(mdbData.mv_size)
|
||||
memcpy(value, mdbData.mv_data, mdbData.mv_size)
|
||||
resultValue = ReadBuffer(memory: value, length: mdbData.mv_size, freeWhenDone: true)
|
||||
} else {
|
||||
if result != MDB_NOTFOUND {
|
||||
print("(LmdbValueBox mdb_get failed with \(result))")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if commit {
|
||||
self.commit()
|
||||
}
|
||||
|
||||
return resultValue
|
||||
}
|
||||
|
||||
public func exists(table: Int32, key: ValueBoxKey) -> Bool {
|
||||
return self.get(table, key: key) != nil
|
||||
}
|
||||
|
||||
public func set(table: Int32, key: ValueBoxKey, value: MemoryBuffer) {
|
||||
var commit = false
|
||||
if self.sharedTxn == nil {
|
||||
self.begin()
|
||||
commit = true
|
||||
}
|
||||
|
||||
var nativeTable: LmdbTable?
|
||||
if let existingTable = self.tables[table] {
|
||||
nativeTable = existingTable
|
||||
} else if let createdTable = self.createTableWithName(table) {
|
||||
nativeTable = createdTable
|
||||
self.tables[table] = createdTable
|
||||
}
|
||||
|
||||
if let nativeTable = nativeTable {
|
||||
var mdbKey = MDB_val()
|
||||
var mdbData = MDB_val()
|
||||
|
||||
mdbKey.mv_data = key.memory
|
||||
mdbKey.mv_size = key.length
|
||||
mdbData.mv_data = value.memory
|
||||
mdbData.mv_size = value.length
|
||||
|
||||
let result = mdb_put(self.sharedTxn, nativeTable.dbi, &mdbKey, &mdbData, 0)
|
||||
if result != MDB_SUCCESS {
|
||||
print("(LmdbValueBox mdb_set failed with \(result))")
|
||||
}
|
||||
}
|
||||
|
||||
if commit {
|
||||
self.commit()
|
||||
}
|
||||
}
|
||||
|
||||
public func remove(table: Int32, key: ValueBoxKey) {
|
||||
var commit = false
|
||||
if self.sharedTxn == nil {
|
||||
self.begin()
|
||||
commit = true
|
||||
}
|
||||
|
||||
var nativeTable: LmdbTable?
|
||||
if let existingTable = self.tables[table] {
|
||||
nativeTable = existingTable
|
||||
} else if let createdTable = self.createTableWithName(table) {
|
||||
nativeTable = createdTable
|
||||
self.tables[table] = createdTable
|
||||
}
|
||||
|
||||
if let nativeTable = nativeTable {
|
||||
var mdbKey = MDB_val()
|
||||
|
||||
mdbKey.mv_data = key.memory
|
||||
mdbKey.mv_size = key.length
|
||||
|
||||
let result = mdb_del(self.sharedTxn, nativeTable.dbi, &mdbKey, nil)
|
||||
if result != MDB_SUCCESS {
|
||||
print("(LmdbValueBox mdb_set failed with \(result))")
|
||||
}
|
||||
}
|
||||
|
||||
if commit {
|
||||
self.commit()
|
||||
}
|
||||
}
|
||||
|
||||
public func drop() {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ public struct MediaId: Hashable, CustomStringConvertible {
|
||||
|
||||
public var hashValue: Int {
|
||||
get {
|
||||
return Int((self.id & 0xffffffff) ^ ((self.id >> 32) & 0xffffffff))
|
||||
return Int((self.id & 0x7fffffff) ^ ((self.id >> 32) & 0x7fffffff))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,5 +67,5 @@ public func ==(lhs: MediaId, rhs: MediaId) -> Bool {
|
||||
}
|
||||
|
||||
public protocol Media: Coding {
|
||||
var id: MediaId { get }
|
||||
var id: MediaId? { get }
|
||||
}
|
||||
|
||||
145
Postbox/MediaBox.swift
Normal file
145
Postbox/MediaBox.swift
Normal file
@@ -0,0 +1,145 @@
|
||||
import Foundation
|
||||
import SwiftSignalKit
|
||||
import Display
|
||||
|
||||
public struct MediaKeyStatus {
|
||||
let size: Int
|
||||
}
|
||||
|
||||
private final class MediaBoxMutexEntry {
|
||||
var mutex = pthread_mutex_t()
|
||||
|
||||
init() {
|
||||
var attr = pthread_mutexattr_t()
|
||||
pthread_mutexattr_init(&attr)
|
||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)
|
||||
pthread_mutex_init(&mutex, &attr)
|
||||
}
|
||||
|
||||
deinit {
|
||||
pthread_mutex_destroy(&mutex)
|
||||
}
|
||||
|
||||
func with(@noescape f: Void -> Void) {
|
||||
pthread_mutex_lock(&self.mutex)
|
||||
f()
|
||||
pthread_mutex_unlock(&self.mutex)
|
||||
}
|
||||
}
|
||||
|
||||
public final class MediaBox {
|
||||
let basePath: String
|
||||
let buffer = WriteBuffer()
|
||||
|
||||
private var mutexEntriesLock = OSSpinLock()
|
||||
private var mutexEntries: [String : MediaBoxMutexEntry] = [:]
|
||||
|
||||
lazy var ensureDirectoryCreated: Void = {
|
||||
try! NSFileManager.defaultManager().createDirectoryAtPath(self.basePath, withIntermediateDirectories: true, attributes: nil)
|
||||
}()
|
||||
|
||||
public init(basePath: String) {
|
||||
self.basePath = basePath
|
||||
}
|
||||
|
||||
private func keyForId(id: MediaId, key: MemoryBuffer) -> String {
|
||||
let string = NSMutableString()
|
||||
string.appendFormat("%d", Int(id.namespace))
|
||||
string.appendFormat("_%lld", Int64(id.id))
|
||||
string.appendString("_\(key)")
|
||||
return string as String
|
||||
}
|
||||
|
||||
private func pathForId(id: MediaId, key: MemoryBuffer) -> String {
|
||||
return "\(self.basePath)/\(self.keyForId(id, key: key))"
|
||||
}
|
||||
|
||||
private func mutexForKey(key: String) -> MediaBoxMutexEntry {
|
||||
let entry: MediaBoxMutexEntry
|
||||
|
||||
OSSpinLockLock(&mutexEntriesLock)
|
||||
if let existingEntry = self.mutexEntries[key] {
|
||||
entry = existingEntry
|
||||
} else {
|
||||
entry = MediaBoxMutexEntry()
|
||||
self.mutexEntries[key] = entry
|
||||
}
|
||||
OSSpinLockUnlock(&mutexEntriesLock)
|
||||
|
||||
return entry
|
||||
}
|
||||
|
||||
func writeId(id: MediaId, key: ReadBuffer, value: NSData) {
|
||||
assertNotOnMainThread()
|
||||
self.mutexForKey(self.keyForId(id, key: key)).with {
|
||||
let _ = self.ensureDirectoryCreated
|
||||
value.writeToFile(self.pathForId(id, key: key), atomically: false)
|
||||
}
|
||||
}
|
||||
|
||||
private func status(id: MediaId, key: ReadBuffer) -> MediaKeyStatus? {
|
||||
assertNotOnMainThread()
|
||||
var value = stat()
|
||||
stat(self.pathForId(id, key: key), &value)
|
||||
return MediaKeyStatus(size: Int(value.st_size))
|
||||
}
|
||||
|
||||
private func read(id: MediaId, key: ReadBuffer) -> NSData? {
|
||||
assertNotOnMainThread()
|
||||
var data: NSData?
|
||||
do {
|
||||
data = try NSData(contentsOfFile: self.pathForId(id, key: key), options: NSDataReadingOptions.DataReadingMappedIfSafe)
|
||||
} catch _ {
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
public func data(id: MediaId, key: ReadBuffer, size: Int, fetch: ((NSData, Int) -> Signal<NSData, NoError>)?) -> Signal<NSData, NoError> {
|
||||
return Signal { subscriber in
|
||||
var cancelled = false
|
||||
let disposable = MetaDisposable()
|
||||
disposable.set(ActionDisposable {
|
||||
cancelled = true
|
||||
})
|
||||
|
||||
self.mutexForKey(self.keyForId(id, key: key)).with {
|
||||
if cancelled {
|
||||
return
|
||||
}
|
||||
|
||||
let status = self.status(id, key: key) ?? MediaKeyStatus(size: 0)
|
||||
|
||||
var currentData: NSData?
|
||||
if status.size > 0 {
|
||||
if let data = self.read(id, key: key) {
|
||||
currentData = data
|
||||
subscriber.putNext(data)
|
||||
}
|
||||
}
|
||||
|
||||
if status.size < size {
|
||||
if let fetch = fetch {
|
||||
disposable.set(fetch(currentData ?? NSData(), status.size).start(next: { [weak self] next in
|
||||
if let strongSelf = self {
|
||||
strongSelf.mutexForKey(strongSelf.keyForId(id, key: key)).with {
|
||||
strongSelf.writeId(id, key: key, value: next)
|
||||
}
|
||||
}
|
||||
subscriber.putNext(next)
|
||||
}, error: { error in
|
||||
subscriber.putError(error)
|
||||
}, completed: {
|
||||
subscriber.putCompletion()
|
||||
}))
|
||||
} else {
|
||||
subscriber.putError(NoError())
|
||||
}
|
||||
} else {
|
||||
subscriber.putCompletion()
|
||||
}
|
||||
}
|
||||
|
||||
return disposable
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
233
Postbox/PostboxCodingUtils.swift
Normal file
233
Postbox/PostboxCodingUtils.swift
Normal file
@@ -0,0 +1,233 @@
|
||||
import Foundation
|
||||
|
||||
func peerViewEntryIndexForBuffer(buffer: ReadBuffer) -> PeerViewEntryIndex {
|
||||
var timestamp: Int32 = 0
|
||||
buffer.read(×tamp, offset: 0, length: 4)
|
||||
timestamp = Int32(bigEndian: timestamp)
|
||||
|
||||
var namespace: Int32 = 0
|
||||
buffer.read(&namespace, offset: 0, length: 4)
|
||||
namespace = Int32(bigEndian: namespace)
|
||||
|
||||
var id: Int32 = 0
|
||||
buffer.read(&id, offset: 0, length: 4)
|
||||
id = Int32(bigEndian: id)
|
||||
|
||||
var peerIdRepresentation: Int64 = 0
|
||||
buffer.read(&peerIdRepresentation, offset: 0, length: 8)
|
||||
peerIdRepresentation = Int64(bigEndian: peerIdRepresentation)
|
||||
|
||||
let peerId = PeerId(peerIdRepresentation)
|
||||
|
||||
return PeerViewEntryIndex(peerId: peerId, messageIndex: MessageIndex(id: MessageId(peerId:peerId, namespace: namespace, id: id), timestamp: timestamp))
|
||||
}
|
||||
|
||||
func bufferForPeerViewEntryIndex(index: PeerViewEntryIndex) -> MemoryBuffer {
|
||||
let buffer = WriteBuffer()
|
||||
|
||||
var timestamp = Int32(bigEndian: index.messageIndex.timestamp)
|
||||
buffer.write(×tamp, offset: 0, length: 4)
|
||||
|
||||
var namespace = Int32(bigEndian: index.messageIndex.id.namespace)
|
||||
buffer.write(&namespace, offset: 0, length: 4)
|
||||
|
||||
var id = Int32(bigEndian: index.messageIndex.id.id)
|
||||
buffer.write(&id, offset: 0, length: 4)
|
||||
|
||||
var peerIdRepresentation = Int64(bigEndian: index.peerId.toInt64())
|
||||
buffer.write(&peerIdRepresentation, offset: 0, length: 8)
|
||||
|
||||
return buffer
|
||||
}
|
||||
|
||||
func messageIdsGroupedByNamespace(ids: [MessageId]) -> [MessageId.Namespace : [MessageId]] {
|
||||
var grouped: [MessageId.Namespace : [MessageId]] = [:]
|
||||
|
||||
for id in ids {
|
||||
if grouped[id.namespace] != nil {
|
||||
grouped[id.namespace]!.append(id)
|
||||
} else {
|
||||
grouped[id.namespace] = [id]
|
||||
}
|
||||
}
|
||||
|
||||
return grouped
|
||||
}
|
||||
|
||||
func mediaIdsGroupedByNamespaceFromMediaArray(mediaArray: [Media]) -> [MediaId.Namespace : [MediaId]] {
|
||||
var grouped: [MediaId.Namespace : [MediaId]] = [:]
|
||||
var seenMediaIds = Set<MediaId>()
|
||||
|
||||
for media in mediaArray {
|
||||
if let id = media.id {
|
||||
if !seenMediaIds.contains(id) {
|
||||
seenMediaIds.insert(id)
|
||||
if grouped[id.namespace] != nil {
|
||||
grouped[id.namespace]!.append(id)
|
||||
} else {
|
||||
grouped[id.namespace] = [id]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return grouped
|
||||
}
|
||||
|
||||
func mediaIdsGroupedByNamespaceFromSet(ids: Set<MediaId>) -> [MediaId.Namespace : [MediaId]] {
|
||||
var grouped: [MediaId.Namespace : [MediaId]] = [:]
|
||||
|
||||
for id in ids {
|
||||
if let _ = grouped[id.namespace] {
|
||||
grouped[id.namespace]!.append(id)
|
||||
} else {
|
||||
grouped[id.namespace] = [id]
|
||||
}
|
||||
}
|
||||
|
||||
return grouped
|
||||
}
|
||||
|
||||
func mediaIdsGroupedByNamespaceFromDictionaryKeys<T>(dict: [MediaId : T]) -> [MediaId.Namespace : [MediaId]] {
|
||||
var grouped: [MediaId.Namespace : [MediaId]] = [:]
|
||||
|
||||
for (id, _) in dict {
|
||||
if grouped[id.namespace] != nil {
|
||||
grouped[id.namespace]!.append(id)
|
||||
} else {
|
||||
grouped[id.namespace] = [id]
|
||||
}
|
||||
}
|
||||
|
||||
return grouped
|
||||
}
|
||||
|
||||
func messagesGroupedByPeerId(messages: [Message]) -> [(PeerId, [Message])] {
|
||||
var grouped: [(PeerId, [Message])] = []
|
||||
|
||||
for message in messages {
|
||||
var i = 0
|
||||
let count = grouped.count
|
||||
var found = false
|
||||
while i < count {
|
||||
if grouped[i].0 == message.id.peerId {
|
||||
grouped[i].1.append(message)
|
||||
found = true
|
||||
break
|
||||
}
|
||||
i++
|
||||
}
|
||||
if !found {
|
||||
grouped.append((message.id.peerId, [message]))
|
||||
}
|
||||
}
|
||||
|
||||
return grouped
|
||||
}
|
||||
|
||||
func messageIdsGroupedByPeerId(messageIds: [MessageId]) -> [PeerId : [MessageId]] {
|
||||
var grouped: [PeerId : [MessageId]] = [:]
|
||||
|
||||
for id in messageIds {
|
||||
if grouped[id.peerId] != nil {
|
||||
grouped[id.peerId]!.append(id)
|
||||
} else {
|
||||
grouped[id.peerId] = [id]
|
||||
}
|
||||
}
|
||||
|
||||
return grouped
|
||||
}
|
||||
|
||||
func blobForMediaIds(ids: [MediaId]) -> Blob {
|
||||
let data = NSMutableData()
|
||||
var version: Int8 = 1
|
||||
data.appendBytes(&version, length: 1)
|
||||
|
||||
var count = Int32(ids.count)
|
||||
data.appendBytes(&count, length:4)
|
||||
|
||||
for id in ids {
|
||||
var mNamespace = id.namespace
|
||||
var mId = id.id
|
||||
data.appendBytes(&mNamespace, length: 4)
|
||||
data.appendBytes(&mId, length: 8)
|
||||
}
|
||||
|
||||
return Blob(data: data)
|
||||
}
|
||||
|
||||
func mediaIdsForBlob(blob: Blob) -> [MediaId] {
|
||||
var ids: [MediaId] = []
|
||||
|
||||
var offset: Int = 0
|
||||
var version = 0
|
||||
blob.data.getBytes(&version, range: NSMakeRange(offset, 1))
|
||||
offset += 1
|
||||
|
||||
if version == 1 {
|
||||
var count: Int32 = 0
|
||||
blob.data.getBytes(&count, range: NSMakeRange(offset, 4))
|
||||
offset += 4
|
||||
|
||||
var i = 0
|
||||
while i < Int(count) {
|
||||
var mNamespace: Int32 = 0
|
||||
var mId: Int64 = 0
|
||||
blob.data.getBytes(&mNamespace, range: NSMakeRange(offset, 4))
|
||||
blob.data.getBytes(&mId, range: NSMakeRange(offset + 4, 8))
|
||||
ids.append(MediaId(namespace: mNamespace, id: mId))
|
||||
offset += 12
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
return ids
|
||||
}
|
||||
|
||||
func memoryBufferForMessageIds(ids: [MessageId]) -> MemoryBuffer {
|
||||
let data = NSMutableData()
|
||||
var version: Int8 = 1
|
||||
data.appendBytes(&version, length: 1)
|
||||
|
||||
var count = Int32(ids.count)
|
||||
data.appendBytes(&count, length:4)
|
||||
|
||||
for id in ids {
|
||||
var mPeerNamespace = id.peerId.namespace
|
||||
var mPeerId = id.peerId.id
|
||||
var mNamespace = id.namespace
|
||||
var mId = id.id
|
||||
data.appendBytes(&mPeerNamespace, length: 4)
|
||||
data.appendBytes(&mPeerId, length: 4)
|
||||
data.appendBytes(&mNamespace, length: 4)
|
||||
data.appendBytes(&mId, length: 4)
|
||||
}
|
||||
|
||||
return MemoryBuffer(data: data)
|
||||
}
|
||||
|
||||
func messageIdsForMemoryBuffer(buffer: MemoryBuffer) -> [MessageId] {
|
||||
var ids: [MessageId] = []
|
||||
|
||||
let readBuffer = ReadBuffer(memoryBufferNoCopy: buffer)
|
||||
|
||||
var count: Int32 = 0
|
||||
readBuffer.read(&count, offset: 0, length: 4)
|
||||
|
||||
var i = 0
|
||||
while i < Int(count) {
|
||||
var mPeerNamespace: Int32 = 0
|
||||
var mPeerId: Int32 = 0
|
||||
var mNamespace: Int32 = 0
|
||||
var mId: Int32 = 0
|
||||
readBuffer.read(&mPeerNamespace, offset: 0, length: 4)
|
||||
readBuffer.read(&mPeerId, offset: 0, length: 4)
|
||||
readBuffer.read(&mNamespace, offset: 0, length: 4)
|
||||
readBuffer.read(&mId, offset: 0, length: 4)
|
||||
ids.append(MessageId(peerId: PeerId(namespace: mPeerNamespace, id: mPeerId), namespace: mNamespace, id: mId))
|
||||
i++
|
||||
}
|
||||
|
||||
return ids
|
||||
}
|
||||
@@ -3,5 +3,6 @@ module sqlcipher {
|
||||
header "sqlcipher/sqlite3ext.h"
|
||||
header "sqlcipher/SQLite-Bridging.h"
|
||||
header "sqlcipher/fts3_tokenizer.h"
|
||||
header "../../submodules/lmdb/libraries/liblmdb/lmdb.h"
|
||||
export *
|
||||
}
|
||||
|
||||
274
Postbox/PostboxTables.swift
Normal file
274
Postbox/PostboxTables.swift
Normal file
@@ -0,0 +1,274 @@
|
||||
import Foundation
|
||||
|
||||
struct Table_Meta {
|
||||
static let id: Int32 = 0
|
||||
|
||||
static func emptyKey() -> ValueBoxKey {
|
||||
return ValueBoxKey(length: 4)
|
||||
}
|
||||
|
||||
static func key(key: ValueBoxKey = Table_Meta.emptyKey()) -> ValueBoxKey {
|
||||
key.setInt32(0, value: 0)
|
||||
return key
|
||||
}
|
||||
}
|
||||
|
||||
struct Table_State {
|
||||
static let id: Int32 = 1
|
||||
|
||||
static func emptyKey() -> ValueBoxKey {
|
||||
return ValueBoxKey(length: 4)
|
||||
}
|
||||
|
||||
static func key(key: ValueBoxKey = Table_State.emptyKey()) -> ValueBoxKey {
|
||||
key.setInt32(0, value: 0)
|
||||
return key
|
||||
}
|
||||
}
|
||||
|
||||
struct Table_Keychain {
|
||||
static let id: Int32 = 2
|
||||
|
||||
static func key(string: String) -> ValueBoxKey {
|
||||
let data = string.dataUsingEncoding(NSUTF8StringEncoding) ?? NSData()
|
||||
let key = ValueBoxKey(length: data.length)
|
||||
memcpy(key.memory, data.bytes, data.length)
|
||||
return key
|
||||
}
|
||||
}
|
||||
|
||||
struct Table_Message {
|
||||
static let id: Int32 = 4
|
||||
|
||||
static func emptyKey() -> ValueBoxKey {
|
||||
return ValueBoxKey(length: 8 + 4 + 4)
|
||||
}
|
||||
|
||||
static func lowerBoundKey(peerId: PeerId, namespace: Int32) -> ValueBoxKey {
|
||||
let key = ValueBoxKey(length: 8 + 4)
|
||||
key.setInt64(0, value: peerId.toInt64())
|
||||
key.setInt32(8, value: namespace)
|
||||
return key
|
||||
}
|
||||
|
||||
static func upperBoundKey(peerId: PeerId, namespace: Int32) -> ValueBoxKey {
|
||||
let key = ValueBoxKey(length: 8 + 4)
|
||||
key.setInt64(0, value: peerId.toInt64())
|
||||
key.setInt32(8, value: namespace)
|
||||
return key.successor
|
||||
}
|
||||
|
||||
static func key(messageId: MessageId, key: ValueBoxKey = Table_Message.emptyKey()) -> ValueBoxKey {
|
||||
key.setInt64(0, value: messageId.peerId.toInt64())
|
||||
key.setInt32(8, value: messageId.namespace)
|
||||
key.setInt32(8 + 4, value: messageId.id)
|
||||
return key
|
||||
}
|
||||
|
||||
static func set(message: Message, encoder: Encoder = Encoder()) -> MemoryBuffer {
|
||||
encoder.reset()
|
||||
encoder.encodeRootObject(message)
|
||||
return encoder.memoryBuffer()
|
||||
}
|
||||
|
||||
static func get(value: ReadBuffer) -> Message? {
|
||||
if let message = Decoder(buffer: value).decodeRootObject() as? Message {
|
||||
return message
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
struct Table_AbsoluteMessageId {
|
||||
static let id: Int32 = 5
|
||||
|
||||
static func emptyKey() -> ValueBoxKey {
|
||||
return ValueBoxKey(length: 4)
|
||||
}
|
||||
|
||||
static func key(id: Int32, key: ValueBoxKey = Table_AbsoluteMessageId.emptyKey()) -> ValueBoxKey {
|
||||
key.setInt32(0, value: id)
|
||||
return key
|
||||
}
|
||||
|
||||
static func set(messageId: MessageId) -> MemoryBuffer {
|
||||
let buffer = WriteBuffer()
|
||||
|
||||
var peerId: Int64 = messageId.peerId.toInt64()
|
||||
buffer.write(&peerId, offset: 0, length: 8)
|
||||
var id_namespace: Int32 = messageId.namespace
|
||||
buffer.write(&id_namespace, offset: 0, length: 4)
|
||||
|
||||
return buffer
|
||||
}
|
||||
|
||||
static func get(id: Int32, value: ReadBuffer) -> MessageId {
|
||||
let offset = value.offset
|
||||
|
||||
var peerId: Int64 = 0
|
||||
var id_namespace: Int32 = 0
|
||||
value.read(&peerId, offset: 0, length: 8)
|
||||
value.read(&id_namespace, offset: 0, length: 4)
|
||||
value.offset = offset
|
||||
|
||||
return MessageId(peerId: PeerId(peerId), namespace: id_namespace, id: id)
|
||||
}
|
||||
}
|
||||
|
||||
struct Table_Media {
|
||||
static let id: Int32 = 6
|
||||
|
||||
static func emptyKey() -> ValueBoxKey {
|
||||
return ValueBoxKey(length: 4 + 8)
|
||||
}
|
||||
|
||||
static func key(id: MediaId, key: ValueBoxKey = Table_Media.emptyKey()) -> ValueBoxKey {
|
||||
key.setInt32(0, value: id.namespace)
|
||||
key.setInt64(4, value: id.id)
|
||||
return key
|
||||
}
|
||||
|
||||
static func set(media: Media, encoder: Encoder = Encoder()) -> MemoryBuffer {
|
||||
encoder.reset()
|
||||
encoder.encodeRootObject(media)
|
||||
return encoder.memoryBuffer()
|
||||
}
|
||||
|
||||
static func get(value: ReadBuffer) -> Media? {
|
||||
let decoder = Decoder(buffer: value)
|
||||
if let media = decoder.decodeRootObject() as? Media {
|
||||
return media
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
struct Table_Media_MessageIds {
|
||||
static let id: Int32 = 3
|
||||
|
||||
static func emptyKey() -> ValueBoxKey {
|
||||
return ValueBoxKey(length: 4 + 8 + 8 + 4 + 4)
|
||||
}
|
||||
|
||||
static func key(id: MediaId, messageId: MessageId, key: ValueBoxKey = Table_Media_MessageIds.emptyKey()) -> ValueBoxKey {
|
||||
key.setInt32(0, value: id.namespace)
|
||||
key.setInt64(4, value: id.id)
|
||||
key.setInt64(4 + 8, value: messageId.peerId.toInt64())
|
||||
key.setInt32(4 + 8 + 8, value: messageId.namespace)
|
||||
key.setInt32(4 + 8 + 8 + 4, value: messageId.id)
|
||||
return key
|
||||
}
|
||||
|
||||
static func lowerBoundKey(id: MediaId) -> ValueBoxKey {
|
||||
let key = ValueBoxKey(length: 4 + 8)
|
||||
key.setInt32(0, value: id.namespace)
|
||||
key.setInt64(4, value: id.id)
|
||||
return key
|
||||
}
|
||||
|
||||
static func upperBoundKey(id: MediaId) -> ValueBoxKey {
|
||||
let key = ValueBoxKey(length: 4 + 8)
|
||||
key.setInt32(0, value: id.namespace)
|
||||
key.setInt64(4, value: id.id)
|
||||
return key.successor
|
||||
}
|
||||
|
||||
static func getMessageId(key: ValueBoxKey) -> MessageId {
|
||||
let peerId = key.getInt64(4 + 8)
|
||||
let messageId_namespace: Int32 = key.getInt32(4 + 8 + 8)
|
||||
let messageId_id: Int32 = key.getInt32(4 + 8 + 8 + 4)
|
||||
return MessageId(peerId: PeerId(peerId), namespace: messageId_namespace, id: messageId_id)
|
||||
}
|
||||
}
|
||||
|
||||
struct Table_Peer {
|
||||
static let id: Int32 = 7
|
||||
|
||||
static func emptyKey() -> ValueBoxKey {
|
||||
return ValueBoxKey(length: 4 + 4)
|
||||
}
|
||||
|
||||
static func key(id: PeerId, key: ValueBoxKey = Table_Peer.emptyKey()) -> ValueBoxKey {
|
||||
key.setInt32(0, value: id.namespace)
|
||||
key.setInt32(4, value: id.id)
|
||||
return key
|
||||
}
|
||||
}
|
||||
|
||||
struct Table_PeerEntry_Sorted {
|
||||
static let id: Int32 = 8
|
||||
|
||||
static func emptyKey() -> ValueBoxKey {
|
||||
return ValueBoxKey(length: 4 + 4 + 4 + 8)
|
||||
}
|
||||
|
||||
static func lowerBoundKey() -> ValueBoxKey {
|
||||
let key = ValueBoxKey(length: 4)
|
||||
key.setInt32(0, value: 0)
|
||||
return key
|
||||
}
|
||||
|
||||
static func upperBoundKey() -> ValueBoxKey {
|
||||
let key = ValueBoxKey(length: 4)
|
||||
key.setInt32(0, value: Int32.max)
|
||||
return key
|
||||
}
|
||||
|
||||
static func key(index: PeerViewEntryIndex, key: ValueBoxKey = Table_PeerEntry_Sorted.emptyKey()) -> ValueBoxKey {
|
||||
key.setInt32(0, value: index.messageIndex.timestamp)
|
||||
key.setInt32(4, value: index.messageIndex.id.namespace)
|
||||
key.setInt32(4 + 4, value: index.messageIndex.id.id)
|
||||
key.setInt64(4 + 4 + 4, value: index.peerId.toInt64())
|
||||
return key
|
||||
}
|
||||
|
||||
static func get(key: ValueBoxKey) -> PeerViewEntryIndex {
|
||||
let messageIndex_timestamp = key.getInt32(0)
|
||||
let messageIndex_id_namespace = key.getInt32(4)
|
||||
let messageIndex_id_id = key.getInt32(4 + 4)
|
||||
let messageIndex_peerId = key.getInt64(4 + 4 + 4)
|
||||
return PeerViewEntryIndex(peerId: PeerId(messageIndex_peerId), messageIndex: MessageIndex(id: MessageId(peerId: PeerId(messageIndex_peerId), namespace: messageIndex_id_namespace, id: messageIndex_id_id), timestamp: messageIndex_timestamp))
|
||||
}
|
||||
}
|
||||
|
||||
struct Table_PeerEntry {
|
||||
static let id: Int32 = 9
|
||||
|
||||
static func emptyKey() -> ValueBoxKey {
|
||||
return ValueBoxKey(length: 8)
|
||||
}
|
||||
|
||||
static func key(peerId: PeerId, key: ValueBoxKey = Table_PeerEntry.emptyKey()) -> ValueBoxKey {
|
||||
key.setInt64(0, value: peerId.toInt64())
|
||||
return key
|
||||
}
|
||||
|
||||
static func set(index: PeerViewEntryIndex) -> MemoryBuffer {
|
||||
let buffer = WriteBuffer()
|
||||
|
||||
var messageId_namespace: Int32 = index.messageIndex.id.namespace
|
||||
var messageId_id: Int32 = index.messageIndex.id.id
|
||||
var timestamp: Int32 = index.messageIndex.timestamp
|
||||
buffer.write(&messageId_namespace, offset: 0, length: 4)
|
||||
buffer.write(&messageId_id, offset: 0, length: 4)
|
||||
buffer.write(×tamp, offset: 0, length: 4)
|
||||
|
||||
return buffer
|
||||
}
|
||||
|
||||
static func get(peerId: PeerId, value: ReadBuffer) -> PeerViewEntryIndex {
|
||||
let offset = value.offset
|
||||
|
||||
var messageId_namespace: Int32 = 0
|
||||
var messageId_id: Int32 = 0
|
||||
var timestamp: Int32 = 0
|
||||
value.read(&messageId_namespace, offset: 0, length: 4)
|
||||
value.read(&messageId_id, offset: 0, length: 4)
|
||||
value.read(×tamp, offset: 0, length: 4)
|
||||
let index = PeerViewEntryIndex(peerId: peerId, messageIndex: MessageIndex(id: MessageId(peerId: peerId, namespace: messageId_namespace, id: messageId_id), timestamp: timestamp))
|
||||
value.offset = offset
|
||||
|
||||
return index
|
||||
}
|
||||
}
|
||||
574
Postbox/SqliteValueBox.swift
Normal file
574
Postbox/SqliteValueBox.swift
Normal file
@@ -0,0 +1,574 @@
|
||||
import Foundation
|
||||
import sqlcipher
|
||||
|
||||
private struct SqlitePreparedStatement {
|
||||
let statement: COpaquePointer
|
||||
|
||||
func bind(index: Int, data: UnsafePointer<Void>, length: Int) {
|
||||
sqlite3_bind_blob(statement, Int32(index), data, Int32(length), nil)
|
||||
}
|
||||
|
||||
func bind(index: Int, number: Int32) {
|
||||
sqlite3_bind_int(statement, Int32(index), number)
|
||||
}
|
||||
|
||||
func reset() {
|
||||
sqlite3_reset(statement)
|
||||
sqlite3_clear_bindings(statement)
|
||||
}
|
||||
|
||||
func step() -> Bool {
|
||||
let result = sqlite3_step(statement)
|
||||
if result != SQLITE_ROW && result != SQLITE_DONE {
|
||||
assertionFailure("Sqlite error \(result)")
|
||||
}
|
||||
return result == SQLITE_ROW
|
||||
}
|
||||
|
||||
func valueAt(index: Int) -> ReadBuffer {
|
||||
let valueLength = sqlite3_column_bytes(statement, Int32(index))
|
||||
let valueData = sqlite3_column_blob(statement, Int32(index))
|
||||
|
||||
let valueMemory = malloc(Int(valueLength))
|
||||
memcpy(valueMemory, valueData, Int(valueLength))
|
||||
return ReadBuffer(memory: valueMemory, length: Int(valueLength), freeWhenDone: true)
|
||||
}
|
||||
|
||||
func keyAt(index: Int) -> ValueBoxKey {
|
||||
let valueLength = sqlite3_column_bytes(statement, Int32(index))
|
||||
let valueData = sqlite3_column_blob(statement, Int32(index))
|
||||
|
||||
let key = ValueBoxKey(length: Int(valueLength))
|
||||
memcpy(key.memory, valueData, Int(valueLength))
|
||||
return key
|
||||
}
|
||||
|
||||
func destroy() {
|
||||
sqlite3_finalize(statement)
|
||||
}
|
||||
}
|
||||
|
||||
public final class SqliteValueBox: ValueBox {
|
||||
private let database: Database
|
||||
private var tables = Set<Int32>()
|
||||
private var getStatements: [Int32 : SqlitePreparedStatement] = [:]
|
||||
private var rangeKeyAscStatementsLimit: [Int32 : SqlitePreparedStatement] = [:]
|
||||
private var rangeKeyAscStatementsNoLimit: [Int32 : SqlitePreparedStatement] = [:]
|
||||
private var rangeKeyDescStatementsLimit: [Int32 : SqlitePreparedStatement] = [:]
|
||||
private var rangeKeyDescStatementsNoLimit: [Int32 : SqlitePreparedStatement] = [:]
|
||||
private var rangeValueAscStatementsLimit: [Int32 : SqlitePreparedStatement] = [:]
|
||||
private var rangeValueAscStatementsNoLimit: [Int32 : SqlitePreparedStatement] = [:]
|
||||
private var rangeValueDescStatementsLimit: [Int32 : SqlitePreparedStatement] = [:]
|
||||
private var rangeValueDescStatementsNoLimit: [Int32 : SqlitePreparedStatement] = [:]
|
||||
private var existsStatements: [Int32 : SqlitePreparedStatement] = [:]
|
||||
private var updateStatements: [Int32 : SqlitePreparedStatement] = [:]
|
||||
private var insertStatements: [Int32 : SqlitePreparedStatement] = [:]
|
||||
private var deleteStatements: [Int32 : SqlitePreparedStatement] = [:]
|
||||
|
||||
public init(basePath: String) {
|
||||
do {
|
||||
try NSFileManager.defaultManager().createDirectoryAtPath(basePath, withIntermediateDirectories: true, attributes: nil)
|
||||
} catch _ { }
|
||||
let path = basePath + "/db_sqlite"
|
||||
self.database = Database(path)
|
||||
|
||||
self.database.adjustChunkSize()
|
||||
self.database.execute("PRAGMA page_size=1024")
|
||||
self.database.execute("PRAGMA cache_size=-2097152")
|
||||
self.database.execute("PRAGMA synchronous=NORMAL")
|
||||
self.database.execute("PRAGMA journal_mode=truncate")
|
||||
self.database.execute("PRAGMA temp_store=MEMORY")
|
||||
//self.database.execute("PRAGMA wal_autocheckpoint=32")
|
||||
//self.database.execute("PRAGMA journal_size_limit=1536")
|
||||
|
||||
let result = self.database.scalar("PRAGMA user_version") as! Int64
|
||||
if result != 1 {
|
||||
self.database.execute("PRAGMA user_version=1")
|
||||
self.database.execute("CREATE TABLE __meta_tables (name INTEGER)")
|
||||
}
|
||||
for row in self.database.prepare("SELECT name FROM __meta_tables").run() {
|
||||
self.tables.insert(Int32(row[0] as! Int64))
|
||||
}
|
||||
}
|
||||
|
||||
public func begin() {
|
||||
self.database.transaction()
|
||||
}
|
||||
|
||||
public func commit() {
|
||||
self.database.commit()
|
||||
}
|
||||
|
||||
private func getStatement(table: Int32, key: ValueBoxKey) -> SqlitePreparedStatement {
|
||||
let resultStatement: SqlitePreparedStatement
|
||||
|
||||
if let statement = self.getStatements[table] {
|
||||
resultStatement = statement
|
||||
} else {
|
||||
var statement: COpaquePointer = nil
|
||||
sqlite3_prepare_v2(self.database.handle, "SELECT value FROM t\(table) WHERE key=?", -1, &statement, nil)
|
||||
let preparedStatement = SqlitePreparedStatement(statement: statement)
|
||||
self.getStatements[table] = preparedStatement
|
||||
resultStatement = preparedStatement
|
||||
}
|
||||
|
||||
resultStatement.reset()
|
||||
|
||||
resultStatement.bind(1, data: key.memory, length: key.length)
|
||||
|
||||
return resultStatement
|
||||
}
|
||||
|
||||
private func rangeKeyAscStatementLimit(table: Int32, start: ValueBoxKey, end: ValueBoxKey, limit: Int) -> SqlitePreparedStatement {
|
||||
let resultStatement: SqlitePreparedStatement
|
||||
|
||||
if let statement = self.rangeKeyAscStatementsLimit[table] {
|
||||
resultStatement = statement
|
||||
} else {
|
||||
var statement: COpaquePointer = nil
|
||||
sqlite3_prepare_v2(self.database.handle, "SELECT key FROM t\(table) WHERE key > ? AND key < ? ORDER BY key ASC LIMIT ?", -1, &statement, nil)
|
||||
let preparedStatement = SqlitePreparedStatement(statement: statement)
|
||||
self.rangeKeyAscStatementsLimit[table] = preparedStatement
|
||||
resultStatement = preparedStatement
|
||||
}
|
||||
resultStatement.reset()
|
||||
|
||||
resultStatement.bind(1, data: start.memory, length: start.length)
|
||||
resultStatement.bind(2, data: end.memory, length: end.length)
|
||||
resultStatement.bind(3, number: Int32(limit))
|
||||
|
||||
return resultStatement
|
||||
}
|
||||
|
||||
private func rangeKeyAscStatementNoLimit(table: Int32, start: ValueBoxKey, end: ValueBoxKey) -> SqlitePreparedStatement {
|
||||
let resultStatement: SqlitePreparedStatement
|
||||
|
||||
if let statement = self.rangeKeyAscStatementsNoLimit[table] {
|
||||
resultStatement = statement
|
||||
} else {
|
||||
var statement: COpaquePointer = nil
|
||||
sqlite3_prepare_v2(self.database.handle, "SELECT key FROM t\(table) WHERE key > ? AND key < ? ORDER BY key ASC", -1, &statement, nil)
|
||||
let preparedStatement = SqlitePreparedStatement(statement: statement)
|
||||
self.rangeKeyAscStatementsNoLimit[table] = preparedStatement
|
||||
resultStatement = preparedStatement
|
||||
}
|
||||
|
||||
resultStatement.reset()
|
||||
|
||||
resultStatement.bind(1, data: start.memory, length: start.length)
|
||||
resultStatement.bind(2, data: end.memory, length: end.length)
|
||||
|
||||
return resultStatement
|
||||
}
|
||||
|
||||
private func rangeKeyDescStatementLimit(table: Int32, start: ValueBoxKey, end: ValueBoxKey, limit: Int) -> SqlitePreparedStatement {
|
||||
let resultStatement: SqlitePreparedStatement
|
||||
|
||||
if let statement = self.rangeKeyDescStatementsLimit[table] {
|
||||
resultStatement = statement
|
||||
} else {
|
||||
var statement: COpaquePointer = nil
|
||||
sqlite3_prepare_v2(self.database.handle, "SELECT key FROM t\(table) WHERE key > ? AND key < ? ORDER BY key DESC LIMIT ?", -1, &statement, nil)
|
||||
let preparedStatement = SqlitePreparedStatement(statement: statement)
|
||||
self.rangeKeyDescStatementsLimit[table] = preparedStatement
|
||||
resultStatement = preparedStatement
|
||||
}
|
||||
|
||||
resultStatement.reset()
|
||||
|
||||
resultStatement.bind(1, data: start.memory, length: start.length)
|
||||
resultStatement.bind(2, data: end.memory, length: end.length)
|
||||
resultStatement.bind(3, number: Int32(limit))
|
||||
|
||||
return resultStatement
|
||||
}
|
||||
|
||||
private func rangeKeyDescStatementNoLimit(table: Int32, start: ValueBoxKey, end: ValueBoxKey) -> SqlitePreparedStatement {
|
||||
let resultStatement: SqlitePreparedStatement
|
||||
|
||||
if let statement = self.rangeKeyDescStatementsNoLimit[table] {
|
||||
resultStatement = statement
|
||||
} else {
|
||||
var statement: COpaquePointer = nil
|
||||
sqlite3_prepare_v2(self.database.handle, "SELECT key FROM t\(table) WHERE key > ? AND key < ? ORDER BY key DESC", -1, &statement, nil)
|
||||
let preparedStatement = SqlitePreparedStatement(statement: statement)
|
||||
self.rangeKeyDescStatementsNoLimit[table] = preparedStatement
|
||||
resultStatement = preparedStatement
|
||||
}
|
||||
|
||||
resultStatement.reset()
|
||||
|
||||
resultStatement.bind(1, data: start.memory, length: start.length)
|
||||
resultStatement.bind(2, data: end.memory, length: end.length)
|
||||
|
||||
return resultStatement
|
||||
}
|
||||
|
||||
private func rangeValueAscStatementLimit(table: Int32, start: ValueBoxKey, end: ValueBoxKey, limit: Int) -> SqlitePreparedStatement {
|
||||
let resultStatement: SqlitePreparedStatement
|
||||
|
||||
if let statement = self.rangeValueAscStatementsLimit[table] {
|
||||
resultStatement = statement
|
||||
} else {
|
||||
var statement: COpaquePointer = nil
|
||||
sqlite3_prepare_v2(self.database.handle, "SELECT key, value FROM t\(table) WHERE key > ? AND key < ? ORDER BY key ASC LIMIT ?", -1, &statement, nil)
|
||||
let preparedStatement = SqlitePreparedStatement(statement: statement)
|
||||
self.rangeValueAscStatementsLimit[table] = preparedStatement
|
||||
resultStatement = preparedStatement
|
||||
}
|
||||
resultStatement.reset()
|
||||
|
||||
resultStatement.bind(1, data: start.memory, length: start.length)
|
||||
resultStatement.bind(2, data: end.memory, length: end.length)
|
||||
resultStatement.bind(3, number: Int32(limit))
|
||||
|
||||
return resultStatement
|
||||
}
|
||||
|
||||
private func rangeValueAscStatementNoLimit(table: Int32, start: ValueBoxKey, end: ValueBoxKey) -> SqlitePreparedStatement {
|
||||
let resultStatement: SqlitePreparedStatement
|
||||
|
||||
if let statement = self.rangeValueAscStatementsNoLimit[table] {
|
||||
resultStatement = statement
|
||||
} else {
|
||||
var statement: COpaquePointer = nil
|
||||
sqlite3_prepare_v2(self.database.handle, "SELECT key, value FROM t\(table) WHERE key > ? AND key < ? ORDER BY key ASC", -1, &statement, nil)
|
||||
let preparedStatement = SqlitePreparedStatement(statement: statement)
|
||||
self.rangeValueAscStatementsNoLimit[table] = preparedStatement
|
||||
resultStatement = preparedStatement
|
||||
}
|
||||
|
||||
resultStatement.reset()
|
||||
|
||||
resultStatement.bind(1, data: start.memory, length: start.length)
|
||||
resultStatement.bind(2, data: end.memory, length: end.length)
|
||||
|
||||
return resultStatement
|
||||
}
|
||||
|
||||
private func rangeValueDescStatementLimit(table: Int32, start: ValueBoxKey, end: ValueBoxKey, limit: Int) -> SqlitePreparedStatement {
|
||||
let resultStatement: SqlitePreparedStatement
|
||||
|
||||
if let statement = self.rangeValueDescStatementsLimit[table] {
|
||||
resultStatement = statement
|
||||
} else {
|
||||
var statement: COpaquePointer = nil
|
||||
sqlite3_prepare_v2(self.database.handle, "SELECT key, value FROM t\(table) WHERE key > ? AND key < ? ORDER BY key DESC LIMIT ?", -1, &statement, nil)
|
||||
let preparedStatement = SqlitePreparedStatement(statement: statement)
|
||||
self.rangeValueDescStatementsLimit[table] = preparedStatement
|
||||
resultStatement = preparedStatement
|
||||
}
|
||||
|
||||
resultStatement.reset()
|
||||
|
||||
resultStatement.bind(1, data: start.memory, length: start.length)
|
||||
resultStatement.bind(2, data: end.memory, length: end.length)
|
||||
resultStatement.bind(3, number: Int32(limit))
|
||||
|
||||
return resultStatement
|
||||
}
|
||||
|
||||
private func rangeValueDescStatementNoLimit(table: Int32, start: ValueBoxKey, end: ValueBoxKey) -> SqlitePreparedStatement {
|
||||
let resultStatement: SqlitePreparedStatement
|
||||
|
||||
if let statement = self.rangeKeyDescStatementsNoLimit[table] {
|
||||
resultStatement = statement
|
||||
} else {
|
||||
var statement: COpaquePointer = nil
|
||||
sqlite3_prepare_v2(self.database.handle, "SELECT key, value FROM t\(table) WHERE key > ? AND key < ? ORDER BY key DESC", -1, &statement, nil)
|
||||
let preparedStatement = SqlitePreparedStatement(statement: statement)
|
||||
self.rangeValueDescStatementsNoLimit[table] = preparedStatement
|
||||
resultStatement = preparedStatement
|
||||
}
|
||||
|
||||
resultStatement.reset()
|
||||
|
||||
resultStatement.bind(1, data: start.memory, length: start.length)
|
||||
resultStatement.bind(2, data: end.memory, length: end.length)
|
||||
|
||||
return resultStatement
|
||||
}
|
||||
|
||||
private func existsStatement(table: Int32, key: ValueBoxKey) -> SqlitePreparedStatement {
|
||||
let resultStatement: SqlitePreparedStatement
|
||||
|
||||
if let statement = self.existsStatements[table] {
|
||||
resultStatement = statement
|
||||
} else {
|
||||
var statement: COpaquePointer = nil
|
||||
sqlite3_prepare_v2(self.database.handle, "SELECT key FROM t\(table) WHERE key=?", -1, &statement, nil)
|
||||
let preparedStatement = SqlitePreparedStatement(statement: statement)
|
||||
self.existsStatements[table] = preparedStatement
|
||||
resultStatement = preparedStatement
|
||||
}
|
||||
|
||||
resultStatement.reset()
|
||||
|
||||
resultStatement.bind(1, data: key.memory, length: key.length)
|
||||
|
||||
return resultStatement
|
||||
}
|
||||
|
||||
private func updateStatement(table: Int32, key: ValueBoxKey, value: MemoryBuffer) -> SqlitePreparedStatement {
|
||||
let resultStatement: SqlitePreparedStatement
|
||||
|
||||
if let statement = self.updateStatements[table] {
|
||||
resultStatement = statement
|
||||
} else {
|
||||
var statement: COpaquePointer = nil
|
||||
sqlite3_prepare_v2(self.database.handle, "UPDATE t\(table) SET value=? WHERE key=?", -1, &statement, nil)
|
||||
let preparedStatement = SqlitePreparedStatement(statement: statement)
|
||||
self.updateStatements[table] = preparedStatement
|
||||
resultStatement = preparedStatement
|
||||
}
|
||||
|
||||
resultStatement.reset()
|
||||
|
||||
resultStatement.bind(1, data: value.memory, length: value.length)
|
||||
resultStatement.bind(2, data: key.memory, length: key.length)
|
||||
|
||||
return resultStatement
|
||||
}
|
||||
|
||||
private func insertStatement(table: Int32, key: ValueBoxKey, value: MemoryBuffer) -> SqlitePreparedStatement {
|
||||
let resultStatement: SqlitePreparedStatement
|
||||
|
||||
if let statement = self.insertStatements[table] {
|
||||
resultStatement = statement
|
||||
} else {
|
||||
var statement: COpaquePointer = nil
|
||||
sqlite3_prepare_v2(self.database.handle, "INSERT INTO t\(table) (key, value) VALUES(?, ?)", -1, &statement, nil)
|
||||
let preparedStatement = SqlitePreparedStatement(statement: statement)
|
||||
self.insertStatements[table] = preparedStatement
|
||||
resultStatement = preparedStatement
|
||||
}
|
||||
|
||||
resultStatement.reset()
|
||||
|
||||
resultStatement.bind(1, data: key.memory, length: key.length)
|
||||
resultStatement.bind(2, data: value.memory, length: value.length)
|
||||
|
||||
return resultStatement
|
||||
}
|
||||
|
||||
private func deleteStatement(table: Int32, key: ValueBoxKey) -> SqlitePreparedStatement {
|
||||
let resultStatement: SqlitePreparedStatement
|
||||
|
||||
if let statement = self.deleteStatements[table] {
|
||||
resultStatement = statement
|
||||
} else {
|
||||
var statement: COpaquePointer = nil
|
||||
sqlite3_prepare_v2(self.database.handle, "DELETE FROM t\(table) WHERE key=?", -1, &statement, nil)
|
||||
let preparedStatement = SqlitePreparedStatement(statement: statement)
|
||||
self.deleteStatements[table] = preparedStatement
|
||||
resultStatement = preparedStatement
|
||||
}
|
||||
|
||||
resultStatement.reset()
|
||||
|
||||
resultStatement.bind(1, data: key.memory, length: key.length)
|
||||
|
||||
return resultStatement
|
||||
}
|
||||
|
||||
public func get(table: Int32, key: ValueBoxKey) -> ReadBuffer? {
|
||||
if self.tables.contains(table) {
|
||||
let statement = self.getStatement(table, key: key)
|
||||
|
||||
var buffer: ReadBuffer?
|
||||
|
||||
while statement.step() {
|
||||
buffer = statement.valueAt(0)
|
||||
break
|
||||
}
|
||||
|
||||
statement.reset()
|
||||
|
||||
return buffer
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
public func exists(table: Int32, key: ValueBoxKey) -> Bool {
|
||||
if let _ = self.get(table, key: key) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
public func range(table: Int32, start: ValueBoxKey, end: ValueBoxKey, @noescape values: (ValueBoxKey, ReadBuffer) -> Bool, limit: Int) {
|
||||
if start == end {
|
||||
return
|
||||
}
|
||||
|
||||
if self.tables.contains(table) {
|
||||
let statement: SqlitePreparedStatement
|
||||
|
||||
if start < end {
|
||||
if limit <= 0 {
|
||||
statement = self.rangeValueAscStatementNoLimit(table, start: start, end: end)
|
||||
} else {
|
||||
statement = self.rangeValueAscStatementLimit(table, start: start, end: end, limit: limit)
|
||||
}
|
||||
} else {
|
||||
if limit <= 0 {
|
||||
statement = self.rangeValueDescStatementNoLimit(table, start: end, end: start)
|
||||
} else {
|
||||
statement = self.rangeValueDescStatementLimit(table, start: end, end: start, limit: limit)
|
||||
}
|
||||
}
|
||||
|
||||
while statement.step() {
|
||||
let key = statement.keyAt(0)
|
||||
let value = statement.valueAt(1)
|
||||
|
||||
if !values(key, value) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
statement.reset()
|
||||
}
|
||||
}
|
||||
|
||||
public func range(table: Int32, start: ValueBoxKey, end: ValueBoxKey, keys: ValueBoxKey -> Bool, limit: Int) {
|
||||
if self.tables.contains(table) {
|
||||
let statement: SqlitePreparedStatement
|
||||
|
||||
if start < end {
|
||||
if limit <= 0 {
|
||||
statement = self.rangeKeyAscStatementNoLimit(table, start: start, end: end)
|
||||
} else {
|
||||
statement = self.rangeKeyAscStatementLimit(table, start: start, end: end, limit: limit)
|
||||
}
|
||||
} else {
|
||||
if limit <= 0 {
|
||||
statement = self.rangeKeyDescStatementNoLimit(table, start: end, end: start)
|
||||
} else {
|
||||
statement = self.rangeKeyDescStatementLimit(table, start: end, end: start, limit: limit)
|
||||
}
|
||||
}
|
||||
|
||||
while statement.step() {
|
||||
let key = statement.keyAt(0)
|
||||
|
||||
if !keys(key) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
statement.reset()
|
||||
}
|
||||
}
|
||||
|
||||
public func set(table: Int32, key: ValueBoxKey, value: MemoryBuffer) {
|
||||
if !self.tables.contains(table) {
|
||||
self.database.execute("CREATE TABLE t\(table) (key BLOB, value BLOB)")
|
||||
self.database.execute("CREATE INDEX t\(table)_key ON t\(table) (key)")
|
||||
self.tables.insert(table)
|
||||
self.database.execute("INSERT INTO __meta_tables(name) VALUES (\(table))")
|
||||
}
|
||||
|
||||
var exists = false
|
||||
let existsStatement = self.existsStatement(table, key: key)
|
||||
if existsStatement.step() {
|
||||
exists = true
|
||||
}
|
||||
existsStatement.reset()
|
||||
|
||||
if exists {
|
||||
let statement = self.updateStatement(table, key: key, value: value)
|
||||
while statement.step() {
|
||||
}
|
||||
statement.reset()
|
||||
} else {
|
||||
let statement = self.insertStatement(table, key: key, value: value)
|
||||
while statement.step() {
|
||||
}
|
||||
statement.reset()
|
||||
}
|
||||
}
|
||||
|
||||
public func remove(table: Int32, key: ValueBoxKey) {
|
||||
if self.tables.contains(table) {
|
||||
|
||||
let statement = self.deleteStatement(table, key: key)
|
||||
while statement.step() {
|
||||
}
|
||||
statement.reset()
|
||||
}
|
||||
}
|
||||
|
||||
public func drop() {
|
||||
for (_, statement) in self.getStatements {
|
||||
statement.destroy()
|
||||
}
|
||||
self.getStatements.removeAll()
|
||||
|
||||
for (_, statement) in self.rangeKeyAscStatementsLimit {
|
||||
statement.destroy()
|
||||
}
|
||||
self.rangeKeyAscStatementsLimit.removeAll()
|
||||
|
||||
for (_, statement) in self.rangeKeyAscStatementsNoLimit {
|
||||
statement.destroy()
|
||||
}
|
||||
self.rangeKeyAscStatementsNoLimit.removeAll()
|
||||
|
||||
for (_, statement) in self.rangeKeyDescStatementsLimit {
|
||||
statement.destroy()
|
||||
}
|
||||
self.rangeKeyDescStatementsLimit.removeAll()
|
||||
|
||||
for (_, statement) in self.rangeKeyDescStatementsNoLimit {
|
||||
statement.destroy()
|
||||
}
|
||||
self.rangeKeyDescStatementsNoLimit.removeAll()
|
||||
|
||||
for (_, statement) in self.rangeValueAscStatementsLimit {
|
||||
statement.destroy()
|
||||
}
|
||||
self.rangeValueAscStatementsLimit.removeAll()
|
||||
|
||||
for (_, statement) in self.rangeValueAscStatementsNoLimit {
|
||||
statement.destroy()
|
||||
}
|
||||
self.rangeValueAscStatementsNoLimit.removeAll()
|
||||
|
||||
for (_, statement) in self.rangeValueDescStatementsLimit {
|
||||
statement.destroy()
|
||||
}
|
||||
self.rangeValueDescStatementsLimit.removeAll()
|
||||
|
||||
for (_, statement) in self.rangeValueDescStatementsNoLimit {
|
||||
statement.destroy()
|
||||
}
|
||||
self.rangeValueDescStatementsNoLimit.removeAll()
|
||||
|
||||
for (_, statement) in self.existsStatements {
|
||||
statement.destroy()
|
||||
}
|
||||
self.existsStatements.removeAll()
|
||||
|
||||
for (_, statement) in self.updateStatements {
|
||||
statement.destroy()
|
||||
}
|
||||
self.updateStatements.removeAll()
|
||||
|
||||
for (_, statement) in self.insertStatements {
|
||||
statement.destroy()
|
||||
}
|
||||
self.insertStatements.removeAll()
|
||||
|
||||
for (_, statement) in self.deleteStatements {
|
||||
statement.destroy()
|
||||
}
|
||||
self.deleteStatements.removeAll()
|
||||
|
||||
for table in self.tables {
|
||||
self.database.execute("DROP TABLE IF EXISTS t\(table)")
|
||||
}
|
||||
self.database.execute("DELETE FROM __meta_tables")
|
||||
}
|
||||
}
|
||||
14
Postbox/ValueBox.swift
Normal file
14
Postbox/ValueBox.swift
Normal file
@@ -0,0 +1,14 @@
|
||||
import Foundation
|
||||
|
||||
public protocol ValueBox {
|
||||
func begin()
|
||||
func commit()
|
||||
|
||||
func range(table: Int32, start: ValueBoxKey, end: ValueBoxKey, @noescape values: (ValueBoxKey, ReadBuffer) -> Bool, limit: Int)
|
||||
func range(table: Int32, start: ValueBoxKey, end: ValueBoxKey, keys: ValueBoxKey -> Bool, limit: Int)
|
||||
func get(table: Int32, key: ValueBoxKey) -> ReadBuffer?
|
||||
func exists(table: Int32, key: ValueBoxKey) -> Bool
|
||||
func set(table: Int32, key: ValueBoxKey, value: MemoryBuffer)
|
||||
func remove(table: Int32, key: ValueBoxKey)
|
||||
func drop()
|
||||
}
|
||||
144
Postbox/ValueBoxKey.swift
Normal file
144
Postbox/ValueBoxKey.swift
Normal file
@@ -0,0 +1,144 @@
|
||||
import Foundation
|
||||
|
||||
private final class ValueBoxKeyImpl {
|
||||
let memory: UnsafeMutablePointer<Void>
|
||||
|
||||
init(memory: UnsafeMutablePointer<Void>) {
|
||||
self.memory = memory
|
||||
}
|
||||
|
||||
deinit {
|
||||
free(self.memory)
|
||||
}
|
||||
}
|
||||
|
||||
public struct ValueBoxKey: Comparable, CustomStringConvertible {
|
||||
public let memory: UnsafeMutablePointer<Void>
|
||||
public let length: Int
|
||||
private let impl: ValueBoxKeyImpl
|
||||
|
||||
public init(length: Int) {
|
||||
self.memory = malloc(length)
|
||||
self.length = length
|
||||
self.impl = ValueBoxKeyImpl(memory: self.memory)
|
||||
}
|
||||
|
||||
public init(_ value: String) {
|
||||
let data = value.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)!
|
||||
self.memory = malloc(data.length)
|
||||
self.length = data.length
|
||||
self.impl = ValueBoxKeyImpl(memory: self.memory)
|
||||
memcpy(self.memory, data.bytes, data.length)
|
||||
}
|
||||
|
||||
public init(_ buffer: MemoryBuffer) {
|
||||
self.memory = malloc(buffer.length)
|
||||
self.length = buffer.length
|
||||
self.impl = ValueBoxKeyImpl(memory: self.memory)
|
||||
memcpy(self.memory, buffer.memory, buffer.length)
|
||||
}
|
||||
|
||||
public func setInt32(offset: Int, value: Int32) {
|
||||
var bigEndianValue = Int32(bigEndian: value)
|
||||
memcpy(self.memory + offset, &bigEndianValue, 4)
|
||||
}
|
||||
|
||||
public func setInt64(offset: Int, value: Int64) {
|
||||
var bigEndianValue = Int64(bigEndian: value)
|
||||
memcpy(self.memory + offset, &bigEndianValue, 8)
|
||||
}
|
||||
|
||||
public func getInt32(offset: Int) -> Int32 {
|
||||
var value: Int32 = 0
|
||||
memcpy(&value, self.memory + offset, 4)
|
||||
return Int32(bigEndian: value)
|
||||
}
|
||||
|
||||
public func getInt64(offset: Int) -> Int64 {
|
||||
var value: Int64 = 0
|
||||
memcpy(&value, self.memory + offset, 8)
|
||||
return Int64(bigEndian: value)
|
||||
}
|
||||
|
||||
public func prefix(length: Int) -> ValueBoxKey {
|
||||
assert(length <= self.length, "length <= self.length")
|
||||
let key = ValueBoxKey(length: length)
|
||||
memcpy(key.memory, self.memory, length)
|
||||
return key
|
||||
}
|
||||
|
||||
public var successor: ValueBoxKey {
|
||||
let key = ValueBoxKey(length: self.length)
|
||||
memcpy(key.memory, self.memory, self.length)
|
||||
let memory = UnsafeMutablePointer<UInt8>(key.memory)
|
||||
var i = self.length - 1
|
||||
while i >= 0 {
|
||||
var byte = memory[i]
|
||||
if byte != 0xff {
|
||||
byte += 1
|
||||
memory[i] = byte
|
||||
break
|
||||
} else {
|
||||
byte = 0
|
||||
memory[i] = byte
|
||||
}
|
||||
i--
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
public var predecessor: ValueBoxKey {
|
||||
let key = ValueBoxKey(length: self.length)
|
||||
memcpy(key.memory, self.memory, self.length)
|
||||
let memory = UnsafeMutablePointer<UInt8>(key.memory)
|
||||
var i = self.length - 1
|
||||
while i >= 0 {
|
||||
var byte = memory[i]
|
||||
if byte != 0x00 {
|
||||
byte -= 1
|
||||
memory[i] = byte
|
||||
break
|
||||
} else {
|
||||
byte = 0xff
|
||||
memory[i] = byte
|
||||
}
|
||||
i--
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
let string = NSMutableString()
|
||||
let memory = UnsafeMutablePointer<UInt8>(self.memory)
|
||||
for i in 0 ..< self.length {
|
||||
let byte: Int = Int(memory[i])
|
||||
string.appendFormat("%02x", byte)
|
||||
}
|
||||
return string as String
|
||||
}
|
||||
}
|
||||
|
||||
public func ==(lhs: ValueBoxKey, rhs: ValueBoxKey) -> Bool {
|
||||
return lhs.length == rhs.length && memcmp(lhs.memory, rhs.memory, lhs.length) == 0
|
||||
}
|
||||
|
||||
private func mdb_cmp_memn(a_memory: UnsafeMutablePointer<Void>, _ a_length: Int, _ b_memory: UnsafeMutablePointer<Void>, _ b_length: Int) -> Int
|
||||
{
|
||||
var diff: Int = 0
|
||||
var len_diff: Int = 0
|
||||
var len: Int = 0
|
||||
|
||||
len = a_length
|
||||
len_diff = a_length - b_length
|
||||
if len_diff > 0 {
|
||||
len = b_length
|
||||
len_diff = 1
|
||||
}
|
||||
|
||||
diff = Int(memcmp(a_memory, b_memory, len))
|
||||
return diff != 0 ? diff : len_diff < 0 ? -1 : len_diff
|
||||
}
|
||||
|
||||
public func <(lhs: ValueBoxKey, rhs: ValueBoxKey) -> Bool {
|
||||
return mdb_cmp_memn(lhs.memory, lhs.length, rhs.memory, rhs.length) < 0
|
||||
}
|
||||
@@ -97,6 +97,14 @@ func ==(lhs: TestKey, rhs: TestKey) -> Bool {
|
||||
return lhs.value == rhs.value
|
||||
}
|
||||
|
||||
class EmptyState: PostboxState {
|
||||
required init(decoder: Decoder) {
|
||||
}
|
||||
|
||||
func encode(encoder: Encoder) {
|
||||
}
|
||||
}
|
||||
|
||||
class SerializationTests: XCTestCase {
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
@@ -107,9 +115,9 @@ class SerializationTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testExample() {
|
||||
declareEncodable(TestParent.self, { TestParent(decoder: $0) })
|
||||
declareEncodable(TestObject.self, { TestObject(decoder: $0) })
|
||||
declareEncodable(TestKey.self, { TestKey(decoder: $0) })
|
||||
declareEncodable(TestParent.self, f: { TestParent(decoder: $0) })
|
||||
declareEncodable(TestObject.self, f: { TestObject(decoder: $0) })
|
||||
declareEncodable(TestKey.self, f: { TestKey(decoder: $0) })
|
||||
|
||||
let encoder = Encoder()
|
||||
encoder.encodeInt32(12345, forKey: "a")
|
||||
@@ -156,4 +164,98 @@ class SerializationTests: XCTestCase {
|
||||
XCTAssert(decoder.decodeInt64ForKey("b") == Int64(12345), "int64 failed")
|
||||
XCTAssert(decoder.decodeInt32ForKey("a") == 12345, "int32 failed")
|
||||
}
|
||||
|
||||
func testIndexBaselinePerformance() {
|
||||
let basePath = "/tmp/postboxtest"
|
||||
do {
|
||||
try NSFileManager.defaultManager().removeItemAtPath(basePath)
|
||||
} catch _ { }
|
||||
let postbox = Postbox<EmptyState>(basePath: basePath, messageNamespaces: [], absoluteIndexedMessageNamespaces: [])
|
||||
postbox._prepareBaselineIndexPerformance()
|
||||
|
||||
measureBlock {
|
||||
postbox._measureBaselineIndexPerformance()
|
||||
}
|
||||
}
|
||||
|
||||
func testBlobReadPerformance() {
|
||||
let basePath = "/tmp/postboxtest"
|
||||
do {
|
||||
try NSFileManager.defaultManager().removeItemAtPath(basePath)
|
||||
} catch _ { }
|
||||
let postbox = Postbox<EmptyState>(basePath: basePath, messageNamespaces: [], absoluteIndexedMessageNamespaces: [])
|
||||
postbox._prepareBlobIndexPerformance()
|
||||
|
||||
measureBlock {
|
||||
postbox._measureBlobReadPerformance()
|
||||
}
|
||||
}
|
||||
|
||||
func testBlobIndexPerformance() {
|
||||
let basePath = "/tmp/postboxtest"
|
||||
do {
|
||||
try NSFileManager.defaultManager().removeItemAtPath(basePath)
|
||||
} catch _ { }
|
||||
let postbox = Postbox<EmptyState>(basePath: basePath, messageNamespaces: [], absoluteIndexedMessageNamespaces: [])
|
||||
postbox._prepareBlobIndexPerformance()
|
||||
|
||||
measureBlock {
|
||||
postbox._measureBlobIndexPerformance()
|
||||
}
|
||||
}
|
||||
|
||||
func testKeys() {
|
||||
let key1 = ValueBoxKey(length: 8)
|
||||
let key2 = ValueBoxKey(length: 8)
|
||||
|
||||
key1.setInt32(0, value: 1)
|
||||
key1.setInt32(4, value: 2)
|
||||
|
||||
key2.setInt32(0, value: 1)
|
||||
key2.setInt32(4, value: 3)
|
||||
|
||||
let lowerBound = ValueBoxKey(length: 4)
|
||||
lowerBound.setInt32(0, value: 0)
|
||||
let upperBound = ValueBoxKey(length: 4)
|
||||
upperBound.setInt32(0, value: 2)
|
||||
|
||||
XCTAssert(key1 > lowerBound, "key1 <= lowerBound")
|
||||
XCTAssert(key1 < upperBound, "key1 >= upperBound")
|
||||
XCTAssert(key2 > lowerBound, "key2 <= lowerBound")
|
||||
XCTAssert(key2 < upperBound, "key2 >= upperBound")
|
||||
|
||||
XCTAssert(key1 < key2, "key1 >= key2")
|
||||
XCTAssert(key1.successor == key2, "key1.next != key2")
|
||||
XCTAssert(key2.predecessor == key1, "key2.previous != key1")
|
||||
}
|
||||
|
||||
func testKeyValue() {
|
||||
let basePath = "/tmp/postboxtest"
|
||||
do {
|
||||
try NSFileManager.defaultManager().removeItemAtPath(basePath)
|
||||
} catch _ { }
|
||||
|
||||
let box = SqliteValueBox(basePath: basePath)
|
||||
box.transaction { transaction in
|
||||
let key = ValueBoxKey(length: 4)
|
||||
let value = WriteBuffer()
|
||||
for i in 1 ... 100 {
|
||||
key.setInt32(0, value: Int32(i))
|
||||
transaction.set("test", key: key, value: value)
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
box.transaction { transaction in
|
||||
let lowerBound = ValueBoxKey(length: 4)
|
||||
lowerBound.setInt32(0, value: 2)
|
||||
let upperBound = ValueBoxKey(length: 4)
|
||||
upperBound.setInt32(0, value: 99)
|
||||
transaction.range("test", start: upperBound, end: lowerBound, values: { key, value in
|
||||
print("\(key.getInt32(0))")
|
||||
return true
|
||||
}, limit: 10)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,826 +0,0 @@
|
||||
import UIKit
|
||||
import XCTest
|
||||
|
||||
import Postbox
|
||||
|
||||
enum TestPeerNamespace: PeerId.Namespace {
|
||||
case User = 0
|
||||
}
|
||||
|
||||
enum TestMessageNamespace: MessageId.Namespace {
|
||||
case Cloud = 0
|
||||
case Local = 1
|
||||
}
|
||||
|
||||
enum TestMediaNamespace: MediaId.Namespace {
|
||||
case Test = 0
|
||||
}
|
||||
|
||||
class TestPeer: Peer {
|
||||
var id: PeerId
|
||||
|
||||
init(id: PeerId) {
|
||||
self.id = id
|
||||
}
|
||||
|
||||
required init(decoder: Decoder) {
|
||||
self.id = PeerId(decoder.decodeInt64ForKey("id"))
|
||||
}
|
||||
|
||||
func encode(encoder: Encoder) {
|
||||
encoder.encodeInt64(self.id.toInt64(), forKey: "id")
|
||||
}
|
||||
}
|
||||
|
||||
class TestMessage: Message {
|
||||
var id: MessageId
|
||||
var authorId: PeerId
|
||||
var date: Int32
|
||||
var text: String
|
||||
var referencedMediaIds: [MediaId]
|
||||
|
||||
var timestamp: Int32 {
|
||||
return date
|
||||
}
|
||||
|
||||
init(id: MessageId, authorId: PeerId, date: Int32, text: String, referencedMediaIds: [MediaId]) {
|
||||
self.id = id
|
||||
self.authorId = authorId
|
||||
self.date = date
|
||||
self.text = text
|
||||
self.referencedMediaIds = referencedMediaIds
|
||||
}
|
||||
|
||||
required init(decoder: Decoder) {
|
||||
self.id = MessageId(decoder.decodeBytesForKeyNoCopy("id"))
|
||||
self.authorId = PeerId(decoder.decodeInt64ForKey("authorId"))
|
||||
self.date = decoder.decodeInt32ForKey("date")
|
||||
self.text = decoder.decodeStringForKey("text")
|
||||
self.referencedMediaIds = MediaId.decodeArrayFromBuffer(decoder.decodeBytesForKeyNoCopy("mediaIds"))
|
||||
}
|
||||
|
||||
func encode(encoder: Encoder) {
|
||||
let buffer = WriteBuffer()
|
||||
self.id.encodeToBuffer(buffer)
|
||||
encoder.encodeBytes(buffer, forKey: "id")
|
||||
buffer.reset()
|
||||
|
||||
encoder.encodeInt64(self.authorId.toInt64(), forKey: "authorId")
|
||||
encoder.encodeInt32(self.date, forKey: "date")
|
||||
encoder.encodeString(self.text, forKey: "text")
|
||||
|
||||
MediaId.encodeArrayToBuffer(self.referencedMediaIds, buffer: buffer)
|
||||
encoder.encodeBytes(buffer, forKey: "mediaIds")
|
||||
buffer.reset()
|
||||
}
|
||||
}
|
||||
|
||||
class TestMedia: Media {
|
||||
var id: MediaId
|
||||
|
||||
init(id: MediaId) {
|
||||
self.id = id
|
||||
}
|
||||
|
||||
required init(decoder: Decoder) {
|
||||
self.id = MediaId(decoder.decodeBytesForKeyNoCopy("id"))
|
||||
}
|
||||
|
||||
func encode(encoder: Encoder) {
|
||||
let buffer = WriteBuffer()
|
||||
self.id.encodeToBuffer(buffer)
|
||||
encoder.encodeBytes(buffer, forKey: "id")
|
||||
}
|
||||
}
|
||||
|
||||
class EmptyState: PostboxState {
|
||||
required init(decoder: Decoder) {
|
||||
}
|
||||
|
||||
func encode(encoder: Encoder) {
|
||||
}
|
||||
}
|
||||
|
||||
class PostboxTests: XCTestCase {
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
func testAddMessages() {
|
||||
declareEncodable(TestMessage.self, { TestMessage(decoder: $0) })
|
||||
declareEncodable(TestMedia.self, { TestMedia(decoder: $0) })
|
||||
|
||||
let ownerId = PeerId(namespace: TestPeerNamespace.User.rawValue, id: 1000)
|
||||
let otherId = PeerId(namespace: TestPeerNamespace.User.rawValue, id: 2000)
|
||||
let messageNamespace = TestMessageNamespace.Cloud.rawValue
|
||||
|
||||
let basePath = "/tmp/postboxtest"
|
||||
NSFileManager.defaultManager().removeItemAtPath(basePath, error: nil)
|
||||
let postbox = Postbox<EmptyState>(basePath: basePath, messageNamespaces: [messageNamespace])
|
||||
(postbox.modify { state -> Void in
|
||||
let testMedia = TestMedia(id: MediaId(namespace: TestMediaNamespace.Test.rawValue, id: 1))
|
||||
for i in 0 ..< 10 {
|
||||
let messageId = MessageId(peerId: otherId, namespace: messageNamespace, id: Int32(i + 1))
|
||||
let message = TestMessage(id: messageId, authorId: ownerId, date: Int32(i + 100), text: "\(i)", referencedMediaIds: [testMedia.id])
|
||||
state.addMessages([message, message], medias: [testMedia])
|
||||
}
|
||||
return
|
||||
}).start()
|
||||
postbox._dumpTables()
|
||||
|
||||
(postbox.modify { state -> Void in
|
||||
var messageIds: [MessageId] = []
|
||||
for i in 0 ..< 5 {
|
||||
let messageId = MessageId(peerId: otherId, namespace: messageNamespace, id: Int32(i + 1))
|
||||
messageIds.append(messageId)
|
||||
}
|
||||
state.deleteMessagesWithIds(messageIds)
|
||||
}).start()
|
||||
postbox._dumpTables();
|
||||
|
||||
(postbox.modify { state -> Void in
|
||||
var messageIds: [MessageId] = []
|
||||
for i in 0 ..< 10 {
|
||||
let messageId = MessageId(peerId: otherId, namespace: messageNamespace, id: Int32(i + 1))
|
||||
messageIds.append(messageId)
|
||||
}
|
||||
state.deleteMessagesWithIds(messageIds)
|
||||
}).start()
|
||||
postbox._dumpTables();
|
||||
}
|
||||
|
||||
func testMessageNamespaceViewAddTail() {
|
||||
let otherId = PeerId(namespace: TestPeerNamespace.User.rawValue, id: 2000)
|
||||
|
||||
let view = MutableMessageView(namespaces: [1, 2], count: 3, earlier: [:], messages: [], later: [:])
|
||||
|
||||
func message(namespace: Int32, id: Int32, timestamp: Int32) -> Message {
|
||||
let messageId = MessageId(peerId: otherId, namespace: namespace, id: Int32(id))
|
||||
return TestMessage(id: messageId, authorId: otherId, date: Int32(timestamp), text: "", referencedMediaIds: [])
|
||||
}
|
||||
|
||||
func add(message: Message) {
|
||||
view.add(message)
|
||||
println("\(view)\n")
|
||||
}
|
||||
|
||||
/*func assertIds(earlier: Int32?, ids: [Int32], later: Int32?) {
|
||||
let otherMessages = ids.reverse().map({ id -> Message in
|
||||
let messageId = MessageId(peerId: otherId, namespace: messageNamespace, id: Int32(id))
|
||||
let message = TestMessage(id: messageId, authorId: otherId, date: Int32(0), text: "", referencedMediaIds: [])
|
||||
return message
|
||||
})
|
||||
let otherView = MessageNamespaceView(namespace: messageNamespace, messages: otherMessages, earlierId: earlier, laterId: later, count: 2)
|
||||
|
||||
XCTAssert(view == otherView, "\(view) != \(otherView)")
|
||||
}*/
|
||||
|
||||
add(message(1, 90, 90))
|
||||
add(message(2, 70, 70))
|
||||
|
||||
add(message(1, 70, 70))
|
||||
|
||||
add(message(1, 80, 80))
|
||||
add(message(2, 100, 100))
|
||||
|
||||
add(message(1, 75, 75))
|
||||
|
||||
add(message(1, 60, 60))
|
||||
}
|
||||
|
||||
func testMessageNamespaceViewAddMiddle1() {
|
||||
let otherId = PeerId(namespace: TestPeerNamespace.User.rawValue, id: 2000)
|
||||
|
||||
func message(namespace: Int32, id: Int32, timestamp: Int32) -> Message {
|
||||
let messageId = MessageId(peerId: otherId, namespace: namespace, id: Int32(id))
|
||||
return TestMessage(id: messageId, authorId: otherId, date: Int32(timestamp), text: "", referencedMediaIds: [])
|
||||
}
|
||||
|
||||
let view = MutableMessageView(namespaces: [1, 2], count: 3, earlier: [1: message(1, 90, 90)], messages: [message(1, 100, 100), message(1, 120, 120), message(1, 140, 140)], later: [1: message(1, 200, 200)])
|
||||
|
||||
func add(message: Message) {
|
||||
view.add(message)
|
||||
println("\(view)\n")
|
||||
}
|
||||
|
||||
add(message(2, 105, 105))
|
||||
add(message(2, 150, 150))
|
||||
add(message(2, 250, 250))
|
||||
add(message(2, 180, 180))
|
||||
}
|
||||
|
||||
func testMessageNamespaceRemoveTail() {
|
||||
let otherId = PeerId(namespace: TestPeerNamespace.User.rawValue, id: 2000)
|
||||
|
||||
var messages: [Message] = []
|
||||
|
||||
func print(messages: [Message]) {
|
||||
var string = ""
|
||||
string += "["
|
||||
var first = true
|
||||
for message in messages {
|
||||
if first {
|
||||
first = false
|
||||
} else {
|
||||
string += ", "
|
||||
}
|
||||
string += "\(message.id.namespace): \(message.id.id)—\(message.timestamp)"
|
||||
}
|
||||
string += "]"
|
||||
println(string)
|
||||
}
|
||||
|
||||
let view = MutableMessageView(namespaces: [1, 2], count: 3, earlier: [:], messages: [], later: [:])
|
||||
|
||||
func id(namespace: Int32, id: Int32) -> MessageId {
|
||||
return MessageId(peerId: otherId, namespace: namespace, id: Int32(id))
|
||||
}
|
||||
|
||||
func message(namespace: Int32, id: Int32, timestamp: Int32) -> Message {
|
||||
let messageId = MessageId(peerId: otherId, namespace: namespace, id: Int32(id))
|
||||
return TestMessage(id: messageId, authorId: otherId, date: Int32(timestamp), text: "", referencedMediaIds: [])
|
||||
}
|
||||
|
||||
func add(message: Message) {
|
||||
view.add(message)
|
||||
println("\(view)")
|
||||
|
||||
messages.append(message)
|
||||
messages.sort({MessageIndex($0) < MessageIndex($1)})
|
||||
|
||||
print(messages)
|
||||
println()
|
||||
}
|
||||
|
||||
func remove(ids: Set<MessageId>) -> MutableMessageView.RemoveContext {
|
||||
let context = view.remove(ids)
|
||||
println("\(view)")
|
||||
|
||||
messages = messages.filter { !ids.contains($0.id) }
|
||||
print(messages)
|
||||
|
||||
return context
|
||||
}
|
||||
|
||||
func fetchEarlier(messages: [Message])(namespace: MessageId.Namespace, id: MessageId.Id?, count: Int) -> [Message] {
|
||||
var filtered: [Message] = []
|
||||
var i = messages.count - 1
|
||||
while i >= 0 && filtered.count < count {
|
||||
if messages[i].id.namespace == namespace && (id == nil || messages[i].id.id < id!) {
|
||||
filtered.append(messages[i])
|
||||
}
|
||||
i--
|
||||
}
|
||||
|
||||
return filtered
|
||||
}
|
||||
|
||||
func fetchLater(messages: [Message])(namespace: MessageId.Namespace, id: MessageId.Id?, count: Int) -> [Message] {
|
||||
var filtered: [Message] = []
|
||||
var i = 0
|
||||
while i < messages.count && filtered.count < count {
|
||||
if messages[i].id.namespace == namespace && (id == nil || messages[i].id.id > id!) {
|
||||
filtered.append(messages[i])
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
||||
return filtered
|
||||
}
|
||||
|
||||
func complete(context: MutableMessageView.RemoveContext) {
|
||||
view.complete(context, fetchEarlier: fetchEarlier(messages), fetchLater: fetchLater(messages))
|
||||
println("\(view)\n")
|
||||
}
|
||||
|
||||
add(message(1, 90, 90))
|
||||
add(message(2, 70, 70))
|
||||
add(message(1, 70, 70))
|
||||
add(message(1, 80, 80))
|
||||
add(message(2, 100, 100))
|
||||
add(message(1, 75, 75))
|
||||
add(message(1, 60, 60))
|
||||
|
||||
println("remove 1:90, 2:100")
|
||||
complete(remove([id(1, 90), id(2, 100)]))
|
||||
|
||||
println("remove 1:60, 2:70")
|
||||
complete(remove([id(1, 60), id(2, 70)]))
|
||||
|
||||
println("remove 1:80, 2:100")
|
||||
complete(remove([id(1, 80), id(2, 100)]))
|
||||
}
|
||||
|
||||
func testMessageNamespaceRemoveAllInside() {
|
||||
let otherId = PeerId(namespace: TestPeerNamespace.User.rawValue, id: 2000)
|
||||
|
||||
var messages: [Message] = []
|
||||
|
||||
func print(messages: [Message]) {
|
||||
var string = ""
|
||||
string += "["
|
||||
var first = true
|
||||
for message in messages {
|
||||
if first {
|
||||
first = false
|
||||
} else {
|
||||
string += ", "
|
||||
}
|
||||
string += "\(message.id.namespace): \(message.id.id)—\(message.timestamp)"
|
||||
}
|
||||
string += "]"
|
||||
println(string)
|
||||
}
|
||||
|
||||
let view = MutableMessageView(namespaces: [1, 2], count: 3, earlier: [:], messages: [], later: [:])
|
||||
|
||||
func id(namespace: Int32, id: Int32) -> MessageId {
|
||||
return MessageId(peerId: otherId, namespace: namespace, id: Int32(id))
|
||||
}
|
||||
|
||||
func message(namespace: Int32, id: Int32, timestamp: Int32) -> Message {
|
||||
let messageId = MessageId(peerId: otherId, namespace: namespace, id: Int32(id))
|
||||
return TestMessage(id: messageId, authorId: otherId, date: Int32(timestamp), text: "", referencedMediaIds: [])
|
||||
}
|
||||
|
||||
func add(message: Message) {
|
||||
view.add(message)
|
||||
println("\(view)")
|
||||
|
||||
messages.append(message)
|
||||
messages.sort({MessageIndex($0) < MessageIndex($1)})
|
||||
|
||||
print(messages)
|
||||
println()
|
||||
}
|
||||
|
||||
func remove(ids: Set<MessageId>) -> MutableMessageView.RemoveContext {
|
||||
let context = view.remove(ids)
|
||||
println("\(view)")
|
||||
|
||||
messages = messages.filter { !ids.contains($0.id) }
|
||||
print(messages)
|
||||
|
||||
return context
|
||||
}
|
||||
|
||||
func fetchEarlier(messages: [Message])(namespace: MessageId.Namespace, id: MessageId.Id?, count: Int) -> [Message] {
|
||||
var filtered: [Message] = []
|
||||
var i = messages.count - 1
|
||||
while i >= 0 && filtered.count < count {
|
||||
if messages[i].id.namespace == namespace && (id == nil || messages[i].id.id < id!) {
|
||||
filtered.append(messages[i])
|
||||
}
|
||||
i--
|
||||
}
|
||||
|
||||
return filtered
|
||||
}
|
||||
|
||||
func fetchLater(messages: [Message])(namespace: MessageId.Namespace, id: MessageId.Id?, count: Int) -> [Message] {
|
||||
var filtered: [Message] = []
|
||||
var i = 0
|
||||
while i < messages.count && filtered.count < count {
|
||||
if messages[i].id.namespace == namespace && (id == nil || messages[i].id.id > id!) {
|
||||
filtered.append(messages[i])
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
||||
return filtered
|
||||
}
|
||||
|
||||
func complete(context: MutableMessageView.RemoveContext) {
|
||||
view.complete(context, fetchEarlier: fetchEarlier(messages), fetchLater: fetchLater(messages))
|
||||
println("\(view)\n")
|
||||
}
|
||||
|
||||
add(message(2, 10, 10))
|
||||
add(message(2, 20, 20))
|
||||
add(message(1, 90, 90))
|
||||
add(message(2, 70, 70))
|
||||
add(message(1, 70, 70))
|
||||
add(message(1, 80, 80))
|
||||
add(message(2, 100, 100))
|
||||
add(message(1, 75, 75))
|
||||
add(message(1, 60, 60))
|
||||
|
||||
println("remove 2:20, 1:80, 1:90, 2:100")
|
||||
complete(remove([id(2, 20), id(1, 80), id(1, 90), id(2, 100)]))
|
||||
}
|
||||
|
||||
func testMessageNamespaceRemoveMiddleSome() {
|
||||
let otherId = PeerId(namespace: TestPeerNamespace.User.rawValue, id: 2000)
|
||||
|
||||
var messages: [Message] = []
|
||||
|
||||
func print(messages: [Message]) {
|
||||
var string = ""
|
||||
string += "["
|
||||
var first = true
|
||||
for message in messages {
|
||||
if first {
|
||||
first = false
|
||||
} else {
|
||||
string += ", "
|
||||
}
|
||||
string += "\(message.id.namespace): \(message.id.id)—\(message.timestamp)"
|
||||
}
|
||||
string += "]"
|
||||
println(string)
|
||||
}
|
||||
|
||||
func id(namespace: Int32, id: Int32) -> MessageId {
|
||||
return MessageId(peerId: otherId, namespace: namespace, id: Int32(id))
|
||||
}
|
||||
|
||||
func message(namespace: Int32, id: Int32, timestamp: Int32) -> Message {
|
||||
let messageId = MessageId(peerId: otherId, namespace: namespace, id: Int32(id))
|
||||
return TestMessage(id: messageId, authorId: otherId, date: Int32(timestamp), text: "", referencedMediaIds: [])
|
||||
}
|
||||
|
||||
messages = [message(2, 1, 1), message(2, 2, 2), message(1, 98, 98), message(1, 99, 99), message(1, 100, 100), message(2, 101, 101), message(1, 102, 102), message(1, 103, 103), message(1, 104, 104), message(1, 105, 105), message(2, 200, 200), message(2, 300, 300)]
|
||||
|
||||
let view = MutableMessageView(namespaces: [1, 2], count: 3, earlier: [1: message(1, 99, 99), 2: message(2, 2, 2)], messages: [message(1, 100, 100), message(2, 101, 101), message(1, 102, 102)], later: [1: message(1, 103, 103), 2: message(2, 200, 200)])
|
||||
|
||||
func add(message: Message) {
|
||||
view.add(message)
|
||||
println("\(view)")
|
||||
|
||||
messages.append(message)
|
||||
messages.sort({MessageIndex($0) < MessageIndex($1)})
|
||||
|
||||
print(messages)
|
||||
println()
|
||||
}
|
||||
|
||||
func remove(ids: Set<MessageId>) -> MutableMessageView.RemoveContext {
|
||||
let context = view.remove(ids)
|
||||
println("\(view)")
|
||||
|
||||
messages = messages.filter { !ids.contains($0.id) }
|
||||
print(messages)
|
||||
|
||||
return context
|
||||
}
|
||||
|
||||
func fetchEarlier(messages: [Message])(namespace: MessageId.Namespace, id: MessageId.Id?, count: Int) -> [Message] {
|
||||
var filtered: [Message] = []
|
||||
var i = messages.count - 1
|
||||
while i >= 0 && filtered.count < count {
|
||||
if messages[i].id.namespace == namespace && (id == nil || messages[i].id.id < id!) {
|
||||
filtered.append(messages[i])
|
||||
}
|
||||
i--
|
||||
}
|
||||
|
||||
return filtered
|
||||
}
|
||||
|
||||
func fetchLater(messages: [Message])(namespace: MessageId.Namespace, id: MessageId.Id?, count: Int) -> [Message] {
|
||||
var filtered: [Message] = []
|
||||
var i = 0
|
||||
while i < messages.count && filtered.count < count {
|
||||
if messages[i].id.namespace == namespace && (id == nil || messages[i].id.id > id!) {
|
||||
filtered.append(messages[i])
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
||||
return filtered
|
||||
}
|
||||
|
||||
func complete(context: MutableMessageView.RemoveContext) {
|
||||
view.complete(context, fetchEarlier: fetchEarlier(messages), fetchLater: fetchLater(messages))
|
||||
println("\(view)\n")
|
||||
}
|
||||
|
||||
print(messages)
|
||||
println("\(view)")
|
||||
|
||||
println("remove 1:100, 2:101")
|
||||
complete(remove([id(1, 100), id(2, 101)]))
|
||||
}
|
||||
|
||||
func testMessageNamespaceRemoveMiddleAllInside() {
|
||||
let otherId = PeerId(namespace: TestPeerNamespace.User.rawValue, id: 2000)
|
||||
|
||||
var messages: [Message] = []
|
||||
|
||||
func print(messages: [Message]) {
|
||||
var string = ""
|
||||
string += "["
|
||||
var first = true
|
||||
for message in messages {
|
||||
if first {
|
||||
first = false
|
||||
} else {
|
||||
string += ", "
|
||||
}
|
||||
string += "\(message.id.namespace): \(message.id.id)—\(message.timestamp)"
|
||||
}
|
||||
string += "]"
|
||||
println(string)
|
||||
}
|
||||
|
||||
func id(namespace: Int32, id: Int32) -> MessageId {
|
||||
return MessageId(peerId: otherId, namespace: namespace, id: Int32(id))
|
||||
}
|
||||
|
||||
func message(namespace: Int32, id: Int32, timestamp: Int32) -> Message {
|
||||
let messageId = MessageId(peerId: otherId, namespace: namespace, id: Int32(id))
|
||||
return TestMessage(id: messageId, authorId: otherId, date: Int32(timestamp), text: "", referencedMediaIds: [])
|
||||
}
|
||||
|
||||
messages = [message(2, 1, 1), message(2, 2, 2), message(1, 98, 98), message(1, 99, 99), message(1, 100, 100), message(2, 101, 101), message(1, 102, 102), message(1, 103, 103), message(1, 104, 104), message(1, 105, 105), message(2, 200, 200), message(2, 300, 300)]
|
||||
|
||||
let view = MutableMessageView(namespaces: [1, 2], count: 3, earlier: [1: message(1, 99, 99), 2: message(2, 2, 2)], messages: [message(1, 100, 100), message(2, 101, 101), message(1, 102, 102)], later: [1: message(1, 103, 103), 2: message(2, 200, 200)])
|
||||
|
||||
func add(message: Message) {
|
||||
view.add(message)
|
||||
println("\(view)")
|
||||
|
||||
messages.append(message)
|
||||
messages.sort({MessageIndex($0) < MessageIndex($1)})
|
||||
|
||||
print(messages)
|
||||
println()
|
||||
}
|
||||
|
||||
func remove(ids: Set<MessageId>) -> MutableMessageView.RemoveContext {
|
||||
let context = view.remove(ids)
|
||||
println("\(view)")
|
||||
|
||||
messages = messages.filter { !ids.contains($0.id) }
|
||||
print(messages)
|
||||
|
||||
return context
|
||||
}
|
||||
|
||||
func fetchEarlier(messages: [Message])(namespace: MessageId.Namespace, id: MessageId.Id?, count: Int) -> [Message] {
|
||||
var filtered: [Message] = []
|
||||
var i = messages.count - 1
|
||||
while i >= 0 && filtered.count < count {
|
||||
if messages[i].id.namespace == namespace && (id == nil || messages[i].id.id < id!) {
|
||||
filtered.append(messages[i])
|
||||
}
|
||||
i--
|
||||
}
|
||||
|
||||
return filtered
|
||||
}
|
||||
|
||||
func fetchLater(messages: [Message])(namespace: MessageId.Namespace, id: MessageId.Id?, count: Int) -> [Message] {
|
||||
var filtered: [Message] = []
|
||||
var i = 0
|
||||
while i < messages.count && filtered.count < count {
|
||||
if messages[i].id.namespace == namespace && (id == nil || messages[i].id.id > id!) {
|
||||
filtered.append(messages[i])
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
||||
return filtered
|
||||
}
|
||||
|
||||
func complete(context: MutableMessageView.RemoveContext) {
|
||||
view.complete(context, fetchEarlier: fetchEarlier(messages), fetchLater: fetchLater(messages))
|
||||
println("\(view)\n")
|
||||
}
|
||||
|
||||
print(messages)
|
||||
println("\(view)\n")
|
||||
|
||||
println("remove 1:100, 2:101, 1: 102")
|
||||
complete(remove([id(1, 100), id(2, 101), id(1, 102)]))
|
||||
}
|
||||
|
||||
func testViewTail() {
|
||||
declareEncodable(TestMessage.self, { TestMessage(decoder: $0) })
|
||||
declareEncodable(TestMedia.self, { TestMedia(decoder: $0) })
|
||||
|
||||
let ownerId = PeerId(namespace: TestPeerNamespace.User.rawValue, id: 1000)
|
||||
let otherId = PeerId(namespace: TestPeerNamespace.User.rawValue, id: 2000)
|
||||
let messageNamespace = TestMessageNamespace.Cloud.rawValue
|
||||
|
||||
let basePath = "/tmp/postboxtest"
|
||||
NSFileManager.defaultManager().removeItemAtPath(basePath, error: nil)
|
||||
let postbox = Postbox<EmptyState>(basePath: basePath, messageNamespaces: [messageNamespace])
|
||||
|
||||
(postbox.modify { state -> Void in
|
||||
let testMedia = TestMedia(id: MediaId(namespace: TestMediaNamespace.Test.rawValue, id: 1))
|
||||
for i in 0 ..< 10 {
|
||||
let messageId = MessageId(peerId: otherId, namespace: messageNamespace, id: Int32(i + 1))
|
||||
let message = TestMessage(id: messageId, authorId: ownerId, date: Int32(i + 100), text: "\(i)", referencedMediaIds: [testMedia.id])
|
||||
state.addMessages([message, message], medias: [testMedia])
|
||||
}
|
||||
return
|
||||
}).start()
|
||||
|
||||
postbox.tailMessageViewForPeerId(otherId, count: 4).start(next: { next in
|
||||
println(next)
|
||||
})
|
||||
|
||||
(postbox.modify { state -> Void in
|
||||
let testMedia = TestMedia(id: MediaId(namespace: TestMediaNamespace.Test.rawValue, id: 1))
|
||||
for i in 10 ..< 15 {
|
||||
let messageId = MessageId(peerId: otherId, namespace: messageNamespace, id: Int32(i + 1))
|
||||
let message = TestMessage(id: messageId, authorId: ownerId, date: Int32(i + 100), text: "\(i)", referencedMediaIds: [testMedia.id])
|
||||
state.addMessages([message, message], medias: [testMedia])
|
||||
}
|
||||
return
|
||||
}).start()
|
||||
|
||||
postbox._sync()
|
||||
}
|
||||
|
||||
func testViewAround() {
|
||||
declareEncodable(TestMessage.self, { TestMessage(decoder: $0) })
|
||||
declareEncodable(TestMedia.self, { TestMedia(decoder: $0) })
|
||||
|
||||
let ownerId = PeerId(namespace: TestPeerNamespace.User.rawValue, id: 1000)
|
||||
let otherId = PeerId(namespace: TestPeerNamespace.User.rawValue, id: 2000)
|
||||
let messageNamespaceCloud = TestMessageNamespace.Cloud.rawValue
|
||||
let messageNamespaceLocal = TestMessageNamespace.Local.rawValue
|
||||
|
||||
let basePath = "/tmp/postboxtest"
|
||||
NSFileManager.defaultManager().removeItemAtPath(basePath, error: nil)
|
||||
let postbox = Postbox<EmptyState>(basePath: basePath, messageNamespaces: [messageNamespaceCloud, messageNamespaceLocal])
|
||||
|
||||
(postbox.modify { state -> Void in
|
||||
let testMedia = TestMedia(id: MediaId(namespace: TestMediaNamespace.Test.rawValue, id: 1))
|
||||
for i in 0 ..< 10 {
|
||||
let messageId = MessageId(peerId: otherId, namespace: messageNamespaceCloud, id: Int32(i + 1))
|
||||
let message = TestMessage(id: messageId, authorId: ownerId, date: Int32(i + 100), text: "\(i)", referencedMediaIds: [testMedia.id])
|
||||
state.addMessages([message, message], medias: [testMedia])
|
||||
}
|
||||
for i in 10 ..< 12 {
|
||||
let messageId = MessageId(peerId: otherId, namespace: messageNamespaceLocal, id: Int32(i + 1))
|
||||
let message = TestMessage(id: messageId, authorId: ownerId, date: Int32(i + 100), text: "\(i)", referencedMediaIds: [testMedia.id])
|
||||
state.addMessages([message, message], medias: [testMedia])
|
||||
}
|
||||
return
|
||||
}).start()
|
||||
|
||||
var i = 1000
|
||||
postbox.aroundMessageViewForPeerId(otherId, id: MessageId(peerId: otherId, namespace: messageNamespaceCloud, id: Int32(i + 1)), count: 3).start(next: { next in
|
||||
println(next)
|
||||
})
|
||||
|
||||
(postbox.modify { state -> Void in
|
||||
let testMedia = TestMedia(id: MediaId(namespace: TestMediaNamespace.Test.rawValue, id: 1))
|
||||
for i in 10 ..< 15 {
|
||||
let messageId = MessageId(peerId: otherId, namespace: messageNamespaceCloud, id: Int32(i + 1))
|
||||
let message = TestMessage(id: messageId, authorId: ownerId, date: Int32(i + 100), text: "\(i)", referencedMediaIds: [testMedia.id])
|
||||
state.addMessages([message, message], medias: [testMedia])
|
||||
}
|
||||
return
|
||||
}).start()
|
||||
|
||||
postbox._sync()
|
||||
}
|
||||
|
||||
func testPeerView() {
|
||||
let view = MutablePeerView(count: 3, earlier: nil, entries: [], later: nil)
|
||||
let messageNamespaceCloud = TestMessageNamespace.Cloud.rawValue
|
||||
let otherId = PeerId(namespace: TestPeerNamespace.User.rawValue, id: 2000)
|
||||
|
||||
var entries: [PeerViewEntry] = []
|
||||
|
||||
func print(entries: [PeerViewEntry]) {
|
||||
var string = ""
|
||||
string += "["
|
||||
var first = true
|
||||
for entry in entries {
|
||||
if first {
|
||||
first = false
|
||||
} else {
|
||||
string += ", "
|
||||
}
|
||||
string += "(p \(entry.peerId.namespace):\(entry.peerId.id), m \(entry.message.id.namespace):\(entry.message.id.id)—\(entry.message.timestamp))"
|
||||
}
|
||||
string += "]"
|
||||
println("\(string)")
|
||||
}
|
||||
|
||||
func add(entry: PeerViewEntry) {
|
||||
entries.append(entry)
|
||||
entries.sort({ PeerViewEntryIndex($0) < PeerViewEntryIndex($1) })
|
||||
|
||||
view.addEntry(entry)
|
||||
|
||||
println("\n\(view)")
|
||||
print(entries)
|
||||
}
|
||||
|
||||
func remove(peerId: PeerId, context: MutablePeerView.RemoveContext? = nil) -> MutablePeerView.RemoveContext {
|
||||
entries = entries.filter({ $0.peerId != peerId })
|
||||
return view.removeEntry(context, peerId: peerId)
|
||||
}
|
||||
|
||||
func fetchEarlier(entries: [PeerViewEntry])(index: PeerViewEntryIndex?, count: Int) -> [PeerViewEntry] {
|
||||
var filtered: [PeerViewEntry] = []
|
||||
var i = entries.count - 1
|
||||
while i >= 0 && filtered.count < count {
|
||||
if index == nil || PeerViewEntryIndex(entries[i]) < index! {
|
||||
filtered.append(entries[i])
|
||||
}
|
||||
i--
|
||||
}
|
||||
|
||||
return filtered
|
||||
}
|
||||
|
||||
func fetchLater(entries: [PeerViewEntry])(index: PeerViewEntryIndex?, count: Int) -> [PeerViewEntry] {
|
||||
var filtered: [PeerViewEntry] = []
|
||||
var i = 0
|
||||
while i < entries.count && filtered.count < count {
|
||||
if index == nil || PeerViewEntryIndex(entries[i]) > index! {
|
||||
filtered.append(entries[i])
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
||||
return filtered
|
||||
}
|
||||
|
||||
func complete(context: MutablePeerView.RemoveContext) {
|
||||
view.complete(context, fetchEarlier: fetchEarlier(entries), fetchLater: fetchLater(entries))
|
||||
}
|
||||
|
||||
println("\(view)")
|
||||
|
||||
for i in 1 ..< 10 {
|
||||
let messageId = MessageId(peerId: otherId, namespace: messageNamespaceCloud, id: Int32(i * 2 * 100))
|
||||
let message = TestMessage(id: messageId, authorId: otherId, date: Int32(i * 2 * 100), text: "\(i)", referencedMediaIds: [])
|
||||
|
||||
add(PeerViewEntry(peer: TestPeer(id: PeerId(namespace: TestPeerNamespace.User.rawValue, id: Int32(i * 2))), message: message))
|
||||
}
|
||||
|
||||
if true {
|
||||
let i = 15
|
||||
let messageId = MessageId(peerId: otherId, namespace: messageNamespaceCloud, id: Int32(i * 100))
|
||||
let message = TestMessage(id: messageId, authorId: otherId, date: Int32(i * 100), text: "\(i)", referencedMediaIds: [])
|
||||
|
||||
add(PeerViewEntry(peer: TestPeer(id: PeerId(namespace: TestPeerNamespace.User.rawValue, id: Int32(i))), message: message))
|
||||
}
|
||||
|
||||
if true {
|
||||
var context = remove(PeerId(namespace: TestPeerNamespace.User.rawValue, id: Int32(15)))
|
||||
context = remove(PeerId(namespace: TestPeerNamespace.User.rawValue, id: Int32(14)), context: context)
|
||||
context = remove(PeerId(namespace: TestPeerNamespace.User.rawValue, id: Int32(16)), context: context)
|
||||
context = remove(PeerId(namespace: TestPeerNamespace.User.rawValue, id: Int32(18)), context: context)
|
||||
println("\n\(view)")
|
||||
print(entries)
|
||||
complete(context)
|
||||
println("\(view)")
|
||||
}
|
||||
}
|
||||
|
||||
func testPeerViewTail() {
|
||||
declareEncodable(TestMessage.self, { TestMessage(decoder: $0) })
|
||||
declareEncodable(TestMedia.self, { TestMedia(decoder: $0) })
|
||||
|
||||
let otherId = PeerId(namespace: TestPeerNamespace.User.rawValue, id: 2000)
|
||||
let messageNamespace = TestMessageNamespace.Cloud.rawValue
|
||||
|
||||
let basePath = "/tmp/postboxtest"
|
||||
NSFileManager.defaultManager().removeItemAtPath(basePath, error: nil)
|
||||
let postbox = Postbox<EmptyState>(basePath: basePath, messageNamespaces: [messageNamespace])
|
||||
|
||||
(postbox.modify { state -> Void in
|
||||
let testMedia = TestMedia(id: MediaId(namespace: TestMediaNamespace.Test.rawValue, id: 1))
|
||||
for i in 0 ..< 10 {
|
||||
let messageId = MessageId(peerId: otherId, namespace: messageNamespace, id: Int32(i + 1))
|
||||
let message = TestMessage(id: messageId, authorId: otherId, date: Int32(i + 100), text: "\(i)", referencedMediaIds: [testMedia.id])
|
||||
state.addMessages([message, message], medias: [testMedia])
|
||||
}
|
||||
return
|
||||
}).start()
|
||||
|
||||
postbox.tailPeerView(3).start(next: { next in
|
||||
println(next)
|
||||
})
|
||||
|
||||
postbox.tailMessageViewForPeerId(otherId, count: 4).start(next: { next in
|
||||
println(next)
|
||||
})
|
||||
|
||||
(postbox.modify { state -> Void in
|
||||
let testMedia = TestMedia(id: MediaId(namespace: TestMediaNamespace.Test.rawValue, id: 1))
|
||||
for i in 10 ..< 15 {
|
||||
let messageId = MessageId(peerId: otherId, namespace: messageNamespace, id: Int32(i + 1))
|
||||
let message = TestMessage(id: messageId, authorId: otherId, date: Int32(i + 100), text: "\(i)", referencedMediaIds: [testMedia.id])
|
||||
state.addMessages([message, message], medias: [testMedia])
|
||||
}
|
||||
return
|
||||
}).start()
|
||||
|
||||
postbox._sync()
|
||||
}
|
||||
}
|
||||
20
submodules/lmdb/libraries/liblmdb/.gitignore
vendored
Normal file
20
submodules/lmdb/libraries/liblmdb/.gitignore
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
mtest
|
||||
mtest[23456]
|
||||
testdb
|
||||
mdb_copy
|
||||
mdb_stat
|
||||
*.[ao]
|
||||
*.so
|
||||
*.exe
|
||||
*[~#]
|
||||
*.bak
|
||||
*.orig
|
||||
*.rej
|
||||
*.gcov
|
||||
*.gcda
|
||||
*.gcno
|
||||
core
|
||||
core.*
|
||||
valgrind.*
|
||||
man/
|
||||
html/
|
||||
112
submodules/lmdb/libraries/liblmdb/CHANGES
Normal file
112
submodules/lmdb/libraries/liblmdb/CHANGES
Normal file
@@ -0,0 +1,112 @@
|
||||
LMDB 0.9 Change Log
|
||||
|
||||
LMDB 0.9.14 Release (2014/09/20)
|
||||
Fix to support 64K page size (ITS#7713)
|
||||
Fix to persist decreased as well as increased mapsizes (ITS#7789)
|
||||
Fix cursor bug when deleting last node of a DUPSORT key
|
||||
Fix mdb_env_info to return FIXEDMAP address
|
||||
Fix ambiguous error code from writing to closed DBI (ITS#7825)
|
||||
Fix mdb_copy copying past end of file (ITS#7886)
|
||||
Fix cursor bugs from page_merge/rebalance
|
||||
Fix to dirty fewer pages in deletes (mdb_page_loose())
|
||||
Fix mdb_dbi_open creating subDBs (ITS#7917)
|
||||
Fix mdb_cursor_get(_DUP) with single value (ITS#7913)
|
||||
Fix Windows compat issues in mtests (ITS#7879)
|
||||
Add compacting variant of mdb_copy
|
||||
Add BigEndian integer key compare code
|
||||
Add mdb_dump/mdb_load utilities
|
||||
|
||||
LMDB 0.9.13 Release (2014/06/18)
|
||||
Fix mdb_page_alloc unlimited overflow page search
|
||||
Documentation
|
||||
Re-fix MDB_CURRENT doc (ITS#7793)
|
||||
Fix MDB_GET_MULTIPLE/MDB_NEXT_MULTIPLE doc
|
||||
|
||||
LMDB 0.9.12 Release (2014/06/13)
|
||||
Fix MDB_GET_BOTH regression (ITS#7875,#7681)
|
||||
Fix MDB_MULTIPLE writing multiple keys (ITS#7834)
|
||||
Fix mdb_rebalance (ITS#7829)
|
||||
Fix mdb_page_split (ITS#7815)
|
||||
Fix md_entries count (ITS#7861,#7828,#7793)
|
||||
Fix MDB_CURRENT (ITS#7793)
|
||||
Fix possible crash on Windows DLL detach
|
||||
Misc code cleanup
|
||||
Documentation
|
||||
mdb_cursor_put: cursor moves on error (ITS#7771)
|
||||
|
||||
|
||||
LMDB 0.9.11 Release (2014/01/15)
|
||||
Add mdb_env_set_assert() (ITS#7775)
|
||||
Fix: invalidate txn on page allocation errors (ITS#7377)
|
||||
Fix xcursor tracking in mdb_cursor_del0() (ITS#7771)
|
||||
Fix corruption from deletes (ITS#7756)
|
||||
Fix Windows/MSVC build issues
|
||||
Raise safe limit of max MDB_MAXKEYSIZE
|
||||
Misc code cleanup
|
||||
Documentation
|
||||
Remove spurious note about non-overlapping flags (ITS#7665)
|
||||
|
||||
LMDB 0.9.10 Release (2013/11/12)
|
||||
Add MDB_NOMEMINIT option
|
||||
Fix mdb_page_split() again (ITS#7589)
|
||||
Fix MDB_NORDAHEAD definition (ITS#7734)
|
||||
Fix mdb_cursor_del() positioning (ITS#7733)
|
||||
Partial fix for larger page sizes (ITS#7713)
|
||||
Fix Windows64/MSVC build issues
|
||||
|
||||
LMDB 0.9.9 Release (2013/10/24)
|
||||
Add mdb_env_get_fd()
|
||||
Add MDB_NORDAHEAD option
|
||||
Add MDB_NOLOCK option
|
||||
Avoid wasting space in mdb_page_split() (ITS#7589)
|
||||
Fix mdb_page_merge() cursor fixup (ITS#7722)
|
||||
Fix mdb_cursor_del() on last delete (ITS#7718)
|
||||
Fix adding WRITEMAP on existing env (ITS#7715)
|
||||
Fix nested txns (ITS#7515)
|
||||
Fix mdb_env_copy() O_DIRECT bug (ITS#7682)
|
||||
Fix mdb_cursor_set(SET_RANGE) return code (ITS#7681)
|
||||
Fix mdb_rebalance() cursor fixup (ITS#7701)
|
||||
Misc code cleanup
|
||||
Documentation
|
||||
Note that by default, readers need write access
|
||||
|
||||
|
||||
LMDB 0.9.8 Release (2013/09/09)
|
||||
Allow mdb_env_set_mapsize() on an open environment
|
||||
Fix mdb_dbi_flags() (ITS#7672)
|
||||
Fix mdb_page_unspill() in nested txns
|
||||
Fix mdb_cursor_get(CURRENT|NEXT) after a delete
|
||||
Fix mdb_cursor_get(DUP) to always return key (ITS#7671)
|
||||
Fix mdb_cursor_del() to always advance to next item (ITS#7670)
|
||||
Fix mdb_cursor_set(SET_RANGE) for tree with single page (ITS#7681)
|
||||
Fix mdb_env_copy() retry open if O_DIRECT fails (ITS#7682)
|
||||
Tweak mdb_page_spill() to be less aggressive
|
||||
Documentation
|
||||
Update caveats since mdb_reader_check() added in 0.9.7
|
||||
|
||||
LMDB 0.9.7 Release (2013/08/17)
|
||||
Don't leave stale lockfile on failed RDONLY open (ITS#7664)
|
||||
Fix mdb_page_split() ref beyond cursor depth
|
||||
Fix read txn data race (ITS#7635)
|
||||
Fix mdb_rebalance (ITS#7536, #7538)
|
||||
Fix mdb_drop() (ITS#7561)
|
||||
Misc DEBUG macro fixes
|
||||
Add MDB_NOTLS envflag
|
||||
Add mdb_env_copyfd()
|
||||
Add mdb_txn_env() (ITS#7660)
|
||||
Add mdb_dbi_flags() (ITS#7661)
|
||||
Add mdb_env_get_maxkeysize()
|
||||
Add mdb_env_reader_list()/mdb_env_reader_check()
|
||||
Add mdb_page_spill/unspill, remove hard txn size limit
|
||||
Use shorter names for semaphores (ITS#7615)
|
||||
Build
|
||||
Fix install target (ITS#7656)
|
||||
Documentation
|
||||
Misc updates for cursors, DB handles, data lifetime
|
||||
|
||||
LMDB 0.9.6 Release (2013/02/25)
|
||||
Many fixes/enhancements
|
||||
|
||||
LMDB 0.9.5 Release (2012/11/30)
|
||||
Renamed from libmdb to liblmdb
|
||||
Many fixes/enhancements
|
||||
20
submodules/lmdb/libraries/liblmdb/COPYRIGHT
Normal file
20
submodules/lmdb/libraries/liblmdb/COPYRIGHT
Normal file
@@ -0,0 +1,20 @@
|
||||
Copyright 2011-2014 Howard Chu, Symas Corp.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted only as authorized by the OpenLDAP
|
||||
Public License.
|
||||
|
||||
A copy of this license is available in the file LICENSE in the
|
||||
top-level directory of the distribution or, alternatively, at
|
||||
<http://www.OpenLDAP.org/license.html>.
|
||||
|
||||
OpenLDAP is a registered trademark of the OpenLDAP Foundation.
|
||||
|
||||
Individual files and/or contributed packages may be copyright by
|
||||
other parties and/or subject to additional restrictions.
|
||||
|
||||
This work also contains materials derived from public sources.
|
||||
|
||||
Additional information about OpenLDAP can be obtained at
|
||||
<http://www.openldap.org/>.
|
||||
1631
submodules/lmdb/libraries/liblmdb/Doxyfile
Normal file
1631
submodules/lmdb/libraries/liblmdb/Doxyfile
Normal file
File diff suppressed because it is too large
Load Diff
47
submodules/lmdb/libraries/liblmdb/LICENSE
Normal file
47
submodules/lmdb/libraries/liblmdb/LICENSE
Normal file
@@ -0,0 +1,47 @@
|
||||
The OpenLDAP Public License
|
||||
Version 2.8, 17 August 2003
|
||||
|
||||
Redistribution and use of this software and associated documentation
|
||||
("Software"), with or without modification, are permitted provided
|
||||
that the following conditions are met:
|
||||
|
||||
1. Redistributions in source form must retain copyright statements
|
||||
and notices,
|
||||
|
||||
2. Redistributions in binary form must reproduce applicable copyright
|
||||
statements and notices, this list of conditions, and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution, and
|
||||
|
||||
3. Redistributions must contain a verbatim copy of this document.
|
||||
|
||||
The OpenLDAP Foundation may revise this license from time to time.
|
||||
Each revision is distinguished by a version number. You may use
|
||||
this Software under terms of this license revision or under the
|
||||
terms of any subsequent revision of the license.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE OPENLDAP FOUNDATION AND ITS
|
||||
CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
SHALL THE OPENLDAP FOUNDATION, ITS CONTRIBUTORS, OR THE AUTHOR(S)
|
||||
OR OWNER(S) OF THE SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
The names of the authors and copyright holders must not be used in
|
||||
advertising or otherwise to promote the sale, use or other dealing
|
||||
in this Software without specific, written prior permission. Title
|
||||
to copyright in this Software shall at all times remain with copyright
|
||||
holders.
|
||||
|
||||
OpenLDAP is a registered trademark of the OpenLDAP Foundation.
|
||||
|
||||
Copyright 1999-2003 The OpenLDAP Foundation, Redwood City,
|
||||
California, USA. All Rights Reserved. Permission to copy and
|
||||
distribute verbatim copies of this document is granted.
|
||||
97
submodules/lmdb/libraries/liblmdb/Makefile
Normal file
97
submodules/lmdb/libraries/liblmdb/Makefile
Normal file
@@ -0,0 +1,97 @@
|
||||
# Makefile for liblmdb (Lightning memory-mapped database library).
|
||||
|
||||
########################################################################
|
||||
# Configuration. The compiler options must enable threaded compilation.
|
||||
#
|
||||
# Preprocessor macros (for CPPFLAGS) of interest...
|
||||
# Note that the defaults should already be correct for most
|
||||
# platforms; you should not need to change any of these.
|
||||
# Read their descriptions in mdb.c if you do:
|
||||
#
|
||||
# - MDB_USE_POSIX_SEM
|
||||
# - MDB_DSYNC
|
||||
# - MDB_FDATASYNC
|
||||
# - MDB_USE_PWRITEV
|
||||
#
|
||||
# There may be other macros in mdb.c of interest. You should
|
||||
# read mdb.c before changing any of them.
|
||||
#
|
||||
CC = gcc
|
||||
W = -W -Wall -Wno-unused-parameter -Wbad-function-cast -Wuninitialized
|
||||
THREADS = -pthread
|
||||
OPT = -O2 -g
|
||||
CFLAGS = $(THREADS) $(OPT) $(W) $(XCFLAGS)
|
||||
LDLIBS =
|
||||
SOLIBS =
|
||||
prefix = /usr/local
|
||||
|
||||
########################################################################
|
||||
|
||||
IHDRS = lmdb.h
|
||||
ILIBS = liblmdb.a liblmdb.so
|
||||
IPROGS = mdb_stat mdb_copy mdb_dump mdb_load
|
||||
IDOCS = mdb_stat.1 mdb_copy.1 mdb_dump.1 mdb_load.1
|
||||
PROGS = $(IPROGS) mtest mtest2 mtest3 mtest4 mtest5
|
||||
all: $(ILIBS) $(PROGS)
|
||||
|
||||
install: $(ILIBS) $(IPROGS) $(IHDRS)
|
||||
for f in $(IPROGS); do cp $$f $(DESTDIR)$(prefix)/bin; done
|
||||
for f in $(ILIBS); do cp $$f $(DESTDIR)$(prefix)/lib; done
|
||||
for f in $(IHDRS); do cp $$f $(DESTDIR)$(prefix)/include; done
|
||||
for f in $(IDOCS); do cp $$f $(DESTDIR)$(prefix)/man/man1; done
|
||||
|
||||
clean:
|
||||
rm -rf $(PROGS) *.[ao] *.so *~ testdb
|
||||
|
||||
test: all
|
||||
mkdir testdb
|
||||
./mtest && ./mdb_stat testdb
|
||||
|
||||
liblmdb.a: mdb.o midl.o
|
||||
ar rs $@ mdb.o midl.o
|
||||
|
||||
liblmdb.so: mdb.o midl.o
|
||||
# $(CC) $(LDFLAGS) -pthread -shared -Wl,-Bsymbolic -o $@ mdb.o midl.o $(SOLIBS)
|
||||
$(CC) $(LDFLAGS) -pthread -shared -o $@ mdb.o midl.o $(SOLIBS)
|
||||
|
||||
mdb_stat: mdb_stat.o liblmdb.a
|
||||
mdb_copy: mdb_copy.o liblmdb.a
|
||||
mdb_dump: mdb_dump.o liblmdb.a
|
||||
mdb_load: mdb_load.o liblmdb.a
|
||||
mtest: mtest.o liblmdb.a
|
||||
mtest2: mtest2.o liblmdb.a
|
||||
mtest3: mtest3.o liblmdb.a
|
||||
mtest4: mtest4.o liblmdb.a
|
||||
mtest5: mtest5.o liblmdb.a
|
||||
mtest6: mtest6.o liblmdb.a
|
||||
|
||||
mdb.o: mdb.c lmdb.h midl.h
|
||||
$(CC) $(CFLAGS) -fPIC $(CPPFLAGS) -c mdb.c
|
||||
|
||||
midl.o: midl.c midl.h
|
||||
$(CC) $(CFLAGS) -fPIC $(CPPFLAGS) -c midl.c
|
||||
|
||||
%: %.o
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) $^ $(LDLIBS) -o $@
|
||||
|
||||
%.o: %.c lmdb.h
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -c $<
|
||||
|
||||
COV_FLAGS=-fprofile-arcs -ftest-coverage
|
||||
COV_OBJS=xmdb.o xmidl.o
|
||||
|
||||
coverage: xmtest
|
||||
for i in mtest*.c [0-9]*.c; do j=`basename \$$i .c`; $(MAKE) $$j.o; \
|
||||
gcc -o x$$j $$j.o $(COV_OBJS) -pthread $(COV_FLAGS); \
|
||||
rm -rf testdb; mkdir testdb; ./x$$j; done
|
||||
gcov xmdb.c
|
||||
gcov xmidl.c
|
||||
|
||||
xmtest: mtest.o xmdb.o xmidl.o
|
||||
gcc -o xmtest mtest.o xmdb.o xmidl.o -pthread $(COV_FLAGS)
|
||||
|
||||
xmdb.o: mdb.c lmdb.h midl.h
|
||||
$(CC) $(CFLAGS) -fPIC $(CPPFLAGS) -O0 $(COV_FLAGS) -c mdb.c -o $@
|
||||
|
||||
xmidl.o: midl.c midl.h
|
||||
$(CC) $(CFLAGS) -fPIC $(CPPFLAGS) -O0 $(COV_FLAGS) -c midl.c -o $@
|
||||
1565
submodules/lmdb/libraries/liblmdb/lmdb.h
Normal file
1565
submodules/lmdb/libraries/liblmdb/lmdb.h
Normal file
File diff suppressed because it is too large
Load Diff
9519
submodules/lmdb/libraries/liblmdb/mdb.c
Normal file
9519
submodules/lmdb/libraries/liblmdb/mdb.c
Normal file
File diff suppressed because it is too large
Load Diff
54
submodules/lmdb/libraries/liblmdb/mdb_copy.1
Normal file
54
submodules/lmdb/libraries/liblmdb/mdb_copy.1
Normal file
@@ -0,0 +1,54 @@
|
||||
.TH MDB_COPY 1 "2014/06/20" "LMDB 0.9.14"
|
||||
.\" Copyright 2012-2014 Howard Chu, Symas Corp. All Rights Reserved.
|
||||
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
|
||||
.SH NAME
|
||||
mdb_copy \- LMDB environment copy tool
|
||||
.SH SYNOPSIS
|
||||
.B mdb_copy
|
||||
[\c
|
||||
.BR \-V ]
|
||||
[\c
|
||||
.BR \-c ]
|
||||
[\c
|
||||
.BR \-n ]
|
||||
.B srcpath
|
||||
[\c
|
||||
.BR dstpath ]
|
||||
.SH DESCRIPTION
|
||||
The
|
||||
.B mdb_copy
|
||||
utility copies an LMDB environment. The environment can
|
||||
be copied regardless of whether it is currently in use.
|
||||
No lockfile is created, since it gets recreated at need.
|
||||
|
||||
If
|
||||
.I dstpath
|
||||
is specified it must be the path of an empty directory
|
||||
for storing the backup. Otherwise, the backup will be
|
||||
written to stdout.
|
||||
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.BR \-V
|
||||
Write the library version number to the standard output, and exit.
|
||||
.TP
|
||||
.BR \-c
|
||||
Compact while copying. Only current data pages will be copied; freed
|
||||
or unused pages will be omitted from the copy. This option will
|
||||
slow down the backup process as it is more CPU-intensive.
|
||||
.TP
|
||||
.BR \-n
|
||||
Open LDMB environment(s) which do not use subdirectories.
|
||||
|
||||
.SH DIAGNOSTICS
|
||||
Exit status is zero if no errors occur.
|
||||
Errors result in a non-zero exit status and
|
||||
a diagnostic message being written to standard error.
|
||||
.SH CAVEATS
|
||||
This utility can trigger significant file size growth if run
|
||||
in parallel with write transactions, because pages which they
|
||||
free during copying cannot be reused until the copy is done.
|
||||
.SH "SEE ALSO"
|
||||
.BR mdb_stat (1)
|
||||
.SH AUTHOR
|
||||
Howard Chu of Symas Corporation <http://www.symas.com>
|
||||
82
submodules/lmdb/libraries/liblmdb/mdb_copy.c
Normal file
82
submodules/lmdb/libraries/liblmdb/mdb_copy.c
Normal file
@@ -0,0 +1,82 @@
|
||||
/* mdb_copy.c - memory-mapped database backup tool */
|
||||
/*
|
||||
* Copyright 2012 Howard Chu, Symas Corp.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted only as authorized by the OpenLDAP
|
||||
* Public License.
|
||||
*
|
||||
* A copy of this license is available in the file LICENSE in the
|
||||
* top-level directory of the distribution or, alternatively, at
|
||||
* <http://www.OpenLDAP.org/license.html>.
|
||||
*/
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#define MDB_STDOUT GetStdHandle(STD_OUTPUT_HANDLE)
|
||||
#else
|
||||
#define MDB_STDOUT 1
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include "lmdb.h"
|
||||
|
||||
static void
|
||||
sighandle(int sig)
|
||||
{
|
||||
}
|
||||
|
||||
int main(int argc,char * argv[])
|
||||
{
|
||||
int rc;
|
||||
MDB_env *env;
|
||||
const char *progname = argv[0], *act;
|
||||
unsigned flags = MDB_RDONLY;
|
||||
unsigned cpflags = 0;
|
||||
|
||||
for (; argc > 1 && argv[1][0] == '-'; argc--, argv++) {
|
||||
if (argv[1][1] == 'n' && argv[1][2] == '\0')
|
||||
flags |= MDB_NOSUBDIR;
|
||||
else if (argv[1][1] == 'c' && argv[1][2] == '\0')
|
||||
cpflags |= MDB_CP_COMPACT;
|
||||
else if (argv[1][1] == 'V' && argv[1][2] == '\0') {
|
||||
printf("%s\n", MDB_VERSION_STRING);
|
||||
exit(0);
|
||||
} else
|
||||
argc = 0;
|
||||
}
|
||||
|
||||
if (argc<2 || argc>3) {
|
||||
fprintf(stderr, "usage: %s [-V] [-c] [-n] srcpath [dstpath]\n", progname);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
#ifdef SIGPIPE
|
||||
signal(SIGPIPE, sighandle);
|
||||
#endif
|
||||
#ifdef SIGHUP
|
||||
signal(SIGHUP, sighandle);
|
||||
#endif
|
||||
signal(SIGINT, sighandle);
|
||||
signal(SIGTERM, sighandle);
|
||||
|
||||
act = "opening environment";
|
||||
rc = mdb_env_create(&env);
|
||||
if (rc == MDB_SUCCESS) {
|
||||
rc = mdb_env_open(env, argv[1], flags, 0664);
|
||||
}
|
||||
if (rc == MDB_SUCCESS) {
|
||||
act = "copying";
|
||||
if (argc == 2)
|
||||
rc = mdb_env_copyfd2(env, MDB_STDOUT, cpflags);
|
||||
else
|
||||
rc = mdb_env_copy2(env, argv[2], cpflags);
|
||||
}
|
||||
if (rc)
|
||||
fprintf(stderr, "%s: %s failed, error %d (%s)\n",
|
||||
progname, act, rc, mdb_strerror(rc));
|
||||
mdb_env_close(env);
|
||||
|
||||
return rc ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
||||
75
submodules/lmdb/libraries/liblmdb/mdb_dump.1
Normal file
75
submodules/lmdb/libraries/liblmdb/mdb_dump.1
Normal file
@@ -0,0 +1,75 @@
|
||||
.TH MDB_DUMP 1 "2014/06/20" "LMDB 0.9.14"
|
||||
.\" Copyright 2014 Howard Chu, Symas Corp. All Rights Reserved.
|
||||
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
|
||||
.SH NAME
|
||||
mdb_dump \- LMDB environment export tool
|
||||
.SH SYNOPSIS
|
||||
.B mdb_dump
|
||||
.BR \ envpath
|
||||
[\c
|
||||
.BR \-V ]
|
||||
[\c
|
||||
.BI \-f \ file\fR]
|
||||
[\c
|
||||
.BR \-l ]
|
||||
[\c
|
||||
.BR \-n ]
|
||||
[\c
|
||||
.BR \-p ]
|
||||
[\c
|
||||
.BR \-a \ |
|
||||
.BI \-s \ subdb\fR]
|
||||
.SH DESCRIPTION
|
||||
The
|
||||
.B mdb_dump
|
||||
utility reads a database and writes its contents to the
|
||||
standard output using a portable flat-text format
|
||||
understood by the
|
||||
.BR mdb_load (1)
|
||||
utility.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.BR \-V
|
||||
Write the library version number to the standard output, and exit.
|
||||
.TP
|
||||
.BR \-f \ file
|
||||
Write to the specified file instead of to the standard output.
|
||||
.TP
|
||||
.BR \-l
|
||||
List the databases stored in the environment. Just the
|
||||
names will be listed, no data will be output.
|
||||
.TP
|
||||
.BR \-n
|
||||
Dump an LMDB database which does not use subdirectories.
|
||||
.TP
|
||||
.BR \-p
|
||||
If characters in either the key or data items are printing characters (as
|
||||
defined by isprint(3)), output them directly. This option permits users to
|
||||
use standard text editors and tools to modify the contents of databases.
|
||||
|
||||
Note: different systems may have different notions about what characters
|
||||
are considered printing characters, and databases dumped in this manner may
|
||||
be less portable to external systems.
|
||||
.TP
|
||||
.BR \-a
|
||||
Dump all of the subdatabases in the environment.
|
||||
.TP
|
||||
.BR \-s \ subdb
|
||||
Dump a specific subdatabase. If no database is specified, only the main database is dumped.
|
||||
.SH DIAGNOSTICS
|
||||
Exit status is zero if no errors occur.
|
||||
Errors result in a non-zero exit status and
|
||||
a diagnostic message being written to standard error.
|
||||
|
||||
Dumping and reloading databases that use user-defined comparison functions
|
||||
will result in new databases that use the default comparison functions.
|
||||
\fBIn this case it is quite likely that the reloaded database will be
|
||||
damaged beyond repair permitting neither record storage nor retrieval.\fP
|
||||
|
||||
The only available workaround is to modify the source for the
|
||||
.BR mdb_load (1)
|
||||
utility to load the database using the correct comparison functions.
|
||||
.SH "SEE ALSO"
|
||||
.BR mdb_load (1)
|
||||
.SH AUTHOR
|
||||
Howard Chu of Symas Corporation <http://www.symas.com>
|
||||
317
submodules/lmdb/libraries/liblmdb/mdb_dump.c
Normal file
317
submodules/lmdb/libraries/liblmdb/mdb_dump.c
Normal file
@@ -0,0 +1,317 @@
|
||||
/* mdb_dump.c - memory-mapped database dump tool */
|
||||
/*
|
||||
* Copyright 2011-2014 Howard Chu, Symas Corp.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted only as authorized by the OpenLDAP
|
||||
* Public License.
|
||||
*
|
||||
* A copy of this license is available in the file LICENSE in the
|
||||
* top-level directory of the distribution or, alternatively, at
|
||||
* <http://www.OpenLDAP.org/license.html>.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include "lmdb.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define Z "I"
|
||||
#else
|
||||
#define Z "z"
|
||||
#endif
|
||||
|
||||
#define PRINT 1
|
||||
static int mode;
|
||||
|
||||
typedef struct flagbit {
|
||||
int bit;
|
||||
char *name;
|
||||
} flagbit;
|
||||
|
||||
flagbit dbflags[] = {
|
||||
{ MDB_REVERSEKEY, "reversekey" },
|
||||
{ MDB_DUPSORT, "dupsort" },
|
||||
{ MDB_INTEGERKEY, "integerkey" },
|
||||
{ MDB_DUPFIXED, "dupfixed" },
|
||||
{ MDB_INTEGERDUP, "integerdup" },
|
||||
{ MDB_REVERSEDUP, "reversedup" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
static volatile sig_atomic_t gotsig;
|
||||
|
||||
static void dumpsig( int sig )
|
||||
{
|
||||
gotsig=1;
|
||||
}
|
||||
|
||||
static const char hexc[] = "0123456789abcdef";
|
||||
|
||||
static void hex(unsigned char c)
|
||||
{
|
||||
putchar(hexc[c >> 4]);
|
||||
putchar(hexc[c & 0xf]);
|
||||
}
|
||||
|
||||
static void text(MDB_val *v)
|
||||
{
|
||||
unsigned char *c, *end;
|
||||
|
||||
putchar(' ');
|
||||
c = v->mv_data;
|
||||
end = c + v->mv_size;
|
||||
while (c < end) {
|
||||
if (isprint(*c)) {
|
||||
putchar(*c);
|
||||
} else {
|
||||
putchar('\\');
|
||||
hex(*c);
|
||||
}
|
||||
c++;
|
||||
}
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
static void byte(MDB_val *v)
|
||||
{
|
||||
unsigned char *c, *end;
|
||||
|
||||
putchar(' ');
|
||||
c = v->mv_data;
|
||||
end = c + v->mv_size;
|
||||
while (c < end) {
|
||||
hex(*c++);
|
||||
}
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
/* Dump in BDB-compatible format */
|
||||
static int dumpit(MDB_txn *txn, MDB_dbi dbi, char *name)
|
||||
{
|
||||
MDB_cursor *mc;
|
||||
MDB_stat ms;
|
||||
MDB_val key, data;
|
||||
MDB_envinfo info;
|
||||
unsigned int flags;
|
||||
int rc, i;
|
||||
|
||||
rc = mdb_dbi_flags(txn, dbi, &flags);
|
||||
if (rc) return rc;
|
||||
|
||||
rc = mdb_stat(txn, dbi, &ms);
|
||||
if (rc) return rc;
|
||||
|
||||
rc = mdb_env_info(mdb_txn_env(txn), &info);
|
||||
if (rc) return rc;
|
||||
|
||||
printf("VERSION=3\n");
|
||||
printf("format=%s\n", mode & PRINT ? "print" : "bytevalue");
|
||||
if (name)
|
||||
printf("database=%s\n", name);
|
||||
printf("type=btree\n");
|
||||
printf("mapsize=%" Z "u\n", info.me_mapsize);
|
||||
if (info.me_mapaddr)
|
||||
printf("mapaddr=%p\n", info.me_mapaddr);
|
||||
printf("maxreaders=%u\n", info.me_maxreaders);
|
||||
|
||||
if (flags & MDB_DUPSORT)
|
||||
printf("duplicates=1\n");
|
||||
|
||||
for (i=0; dbflags[i].bit; i++)
|
||||
if (flags & dbflags[i].bit)
|
||||
printf("%s=1\n", dbflags[i].name);
|
||||
|
||||
printf("db_pagesize=%d\n", ms.ms_psize);
|
||||
printf("HEADER=END\n");
|
||||
|
||||
rc = mdb_cursor_open(txn, dbi, &mc);
|
||||
if (rc) return rc;
|
||||
|
||||
while ((rc = mdb_cursor_get(mc, &key, &data, MDB_NEXT) == MDB_SUCCESS)) {
|
||||
if (gotsig) {
|
||||
rc = EINTR;
|
||||
break;
|
||||
}
|
||||
if (mode & PRINT) {
|
||||
text(&key);
|
||||
text(&data);
|
||||
} else {
|
||||
byte(&key);
|
||||
byte(&data);
|
||||
}
|
||||
}
|
||||
printf("DATA=END\n");
|
||||
if (rc == MDB_NOTFOUND)
|
||||
rc = MDB_SUCCESS;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void usage(char *prog)
|
||||
{
|
||||
fprintf(stderr, "usage: %s dbpath [-V] [-f output] [-l] [-n] [-p] [-a|-s subdb]\n", prog);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int i, rc;
|
||||
MDB_env *env;
|
||||
MDB_txn *txn;
|
||||
MDB_dbi dbi;
|
||||
char *prog = argv[0];
|
||||
char *envname;
|
||||
char *subname = NULL;
|
||||
int alldbs = 0, envflags = 0, list = 0;
|
||||
|
||||
if (argc < 2) {
|
||||
usage(prog);
|
||||
}
|
||||
|
||||
/* -a: dump main DB and all subDBs
|
||||
* -s: dump only the named subDB
|
||||
* -n: use NOSUBDIR flag on env_open
|
||||
* -p: use printable characters
|
||||
* -f: write to file instead of stdout
|
||||
* -V: print version and exit
|
||||
* (default) dump only the main DB
|
||||
*/
|
||||
while ((i = getopt(argc, argv, "af:lnps:V")) != EOF) {
|
||||
switch(i) {
|
||||
case 'V':
|
||||
printf("%s\n", MDB_VERSION_STRING);
|
||||
exit(0);
|
||||
break;
|
||||
case 'l':
|
||||
list = 1;
|
||||
/*FALLTHROUGH*/;
|
||||
case 'a':
|
||||
if (subname)
|
||||
usage(prog);
|
||||
alldbs++;
|
||||
break;
|
||||
case 'f':
|
||||
if (freopen(optarg, "w", stdout) == NULL) {
|
||||
fprintf(stderr, "%s: %s: reopen: %s\n",
|
||||
prog, optarg, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
case 'n':
|
||||
envflags |= MDB_NOSUBDIR;
|
||||
break;
|
||||
case 'p':
|
||||
mode |= PRINT;
|
||||
break;
|
||||
case 's':
|
||||
if (alldbs)
|
||||
usage(prog);
|
||||
subname = optarg;
|
||||
break;
|
||||
default:
|
||||
usage(prog);
|
||||
}
|
||||
}
|
||||
|
||||
if (optind != argc - 1)
|
||||
usage(prog);
|
||||
|
||||
#ifdef SIGPIPE
|
||||
signal(SIGPIPE, dumpsig);
|
||||
#endif
|
||||
#ifdef SIGHUP
|
||||
signal(SIGHUP, dumpsig);
|
||||
#endif
|
||||
signal(SIGINT, dumpsig);
|
||||
signal(SIGTERM, dumpsig);
|
||||
|
||||
envname = argv[optind];
|
||||
rc = mdb_env_create(&env);
|
||||
if (rc) {
|
||||
fprintf(stderr, "mdb_env_create failed, error %d %s\n", rc, mdb_strerror(rc));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (alldbs || subname) {
|
||||
mdb_env_set_maxdbs(env, 2);
|
||||
}
|
||||
|
||||
rc = mdb_env_open(env, envname, envflags | MDB_RDONLY, 0664);
|
||||
if (rc) {
|
||||
fprintf(stderr, "mdb_env_open failed, error %d %s\n", rc, mdb_strerror(rc));
|
||||
goto env_close;
|
||||
}
|
||||
|
||||
rc = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn);
|
||||
if (rc) {
|
||||
fprintf(stderr, "mdb_txn_begin failed, error %d %s\n", rc, mdb_strerror(rc));
|
||||
goto env_close;
|
||||
}
|
||||
|
||||
rc = mdb_open(txn, subname, 0, &dbi);
|
||||
if (rc) {
|
||||
fprintf(stderr, "mdb_open failed, error %d %s\n", rc, mdb_strerror(rc));
|
||||
goto txn_abort;
|
||||
}
|
||||
|
||||
if (alldbs) {
|
||||
MDB_cursor *cursor;
|
||||
MDB_val key;
|
||||
int count = 0;
|
||||
|
||||
rc = mdb_cursor_open(txn, dbi, &cursor);
|
||||
if (rc) {
|
||||
fprintf(stderr, "mdb_cursor_open failed, error %d %s\n", rc, mdb_strerror(rc));
|
||||
goto txn_abort;
|
||||
}
|
||||
while ((rc = mdb_cursor_get(cursor, &key, NULL, MDB_NEXT_NODUP)) == 0) {
|
||||
char *str;
|
||||
MDB_dbi db2;
|
||||
if (memchr(key.mv_data, '\0', key.mv_size))
|
||||
continue;
|
||||
count++;
|
||||
str = malloc(key.mv_size+1);
|
||||
memcpy(str, key.mv_data, key.mv_size);
|
||||
str[key.mv_size] = '\0';
|
||||
rc = mdb_open(txn, str, 0, &db2);
|
||||
if (rc == MDB_SUCCESS) {
|
||||
if (list) {
|
||||
printf("%s\n", str);
|
||||
list++;
|
||||
} else {
|
||||
rc = dumpit(txn, db2, str);
|
||||
if (rc)
|
||||
break;
|
||||
}
|
||||
mdb_close(env, db2);
|
||||
}
|
||||
free(str);
|
||||
if (rc) continue;
|
||||
}
|
||||
mdb_cursor_close(cursor);
|
||||
if (!count) {
|
||||
fprintf(stderr, "%s: %s does not contain multiple databases\n", prog, envname);
|
||||
rc = MDB_NOTFOUND;
|
||||
} else if (rc == MDB_NOTFOUND) {
|
||||
rc = MDB_SUCCESS;
|
||||
}
|
||||
} else {
|
||||
rc = dumpit(txn, dbi, subname);
|
||||
}
|
||||
if (rc && rc != MDB_NOTFOUND)
|
||||
fprintf(stderr, "%s: %s: %s\n", prog, envname, mdb_strerror(rc));
|
||||
|
||||
mdb_close(env, dbi);
|
||||
txn_abort:
|
||||
mdb_txn_abort(txn);
|
||||
env_close:
|
||||
mdb_env_close(env);
|
||||
|
||||
return rc ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
||||
77
submodules/lmdb/libraries/liblmdb/mdb_load.1
Normal file
77
submodules/lmdb/libraries/liblmdb/mdb_load.1
Normal file
@@ -0,0 +1,77 @@
|
||||
.TH MDB_LOAD 1 "2014/06/20" "LMDB 0.9.14"
|
||||
.\" Copyright 2014 Howard Chu, Symas Corp. All Rights Reserved.
|
||||
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
|
||||
.SH NAME
|
||||
mdb_load \- LMDB environment import tool
|
||||
.SH SYNOPSIS
|
||||
.B mdb_load
|
||||
.BR \ envpath
|
||||
[\c
|
||||
.BR \-V ]
|
||||
[\c
|
||||
.BI \-f \ file\fR]
|
||||
[\c
|
||||
.BR \-n ]
|
||||
[\c
|
||||
.BI \-s \ subdb\fR]
|
||||
[\c
|
||||
.BR \-N ]
|
||||
[\c
|
||||
.BR \-T ]
|
||||
.SH DESCRIPTION
|
||||
The
|
||||
.B mdb_load
|
||||
utility reads from the standard input and loads it into the
|
||||
LMDB environment
|
||||
.BR envpath .
|
||||
|
||||
The input to
|
||||
.B mdb_load
|
||||
must be in the output format specified by the
|
||||
.BR mdb_dump (1)
|
||||
utility or as specified by the
|
||||
.B -T
|
||||
option below.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.BR \-V
|
||||
Write the library version number to the standard output, and exit.
|
||||
.TP
|
||||
.BR \-f \ file
|
||||
Read from the specified file instead of from the standard input.
|
||||
.TP
|
||||
.BR \-n
|
||||
Load an LMDB database which does not use subdirectories.
|
||||
.TP
|
||||
.BR \-s \ subdb
|
||||
Load a specific subdatabase. If no database is specified, data is loaded into the main database.
|
||||
.TP
|
||||
.BR \-N
|
||||
Don't overwrite existing records when loading into an already existing database; just skip them.
|
||||
.TP
|
||||
.BR \-T
|
||||
Load data from simple text files. The input must be paired lines of text, where the first
|
||||
line of the pair is the key item, and the second line of the pair is its corresponding
|
||||
data item.
|
||||
|
||||
A simple escape mechanism, where newline and backslash (\\) characters are special, is
|
||||
applied to the text input. Newline characters are interpreted as record separators.
|
||||
Backslash characters in the text will be interpreted in one of two ways: If the backslash
|
||||
character precedes another backslash character, the pair will be interpreted as a literal
|
||||
backslash. If the backslash character precedes any other character, the two characters
|
||||
following the backslash will be interpreted as a hexadecimal specification of a single
|
||||
character; for example, \\0a is a newline character in the ASCII character set.
|
||||
|
||||
For this reason, any backslash or newline characters that naturally occur in the text
|
||||
input must be escaped to avoid misinterpretation by
|
||||
.BR mdb_load .
|
||||
|
||||
.SH DIAGNOSTICS
|
||||
Exit status is zero if no errors occur.
|
||||
Errors result in a non-zero exit status and
|
||||
a diagnostic message being written to standard error.
|
||||
|
||||
.SH "SEE ALSO"
|
||||
.BR mdb_dump (1)
|
||||
.SH AUTHOR
|
||||
Howard Chu of Symas Corporation <http://www.symas.com>
|
||||
452
submodules/lmdb/libraries/liblmdb/mdb_load.c
Normal file
452
submodules/lmdb/libraries/liblmdb/mdb_load.c
Normal file
@@ -0,0 +1,452 @@
|
||||
/* mdb_load.c - memory-mapped database load tool */
|
||||
/*
|
||||
* Copyright 2011-2014 Howard Chu, Symas Corp.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted only as authorized by the OpenLDAP
|
||||
* Public License.
|
||||
*
|
||||
* A copy of this license is available in the file LICENSE in the
|
||||
* top-level directory of the distribution or, alternatively, at
|
||||
* <http://www.OpenLDAP.org/license.html>.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <unistd.h>
|
||||
#include "lmdb.h"
|
||||
|
||||
#define PRINT 1
|
||||
#define NOHDR 2
|
||||
static int mode;
|
||||
|
||||
static char *subname = NULL;
|
||||
|
||||
static size_t lineno;
|
||||
static int version;
|
||||
|
||||
static int flags;
|
||||
|
||||
static char *prog;
|
||||
|
||||
static int Eof;
|
||||
|
||||
static MDB_envinfo info;
|
||||
|
||||
static MDB_val kbuf, dbuf;
|
||||
|
||||
#ifdef _WIN32
|
||||
#define Z "I"
|
||||
#else
|
||||
#define Z "z"
|
||||
#endif
|
||||
|
||||
#define STRLENOF(s) (sizeof(s)-1)
|
||||
|
||||
typedef struct flagbit {
|
||||
int bit;
|
||||
char *name;
|
||||
int len;
|
||||
} flagbit;
|
||||
|
||||
#define S(s) s, STRLENOF(s)
|
||||
|
||||
flagbit dbflags[] = {
|
||||
{ MDB_REVERSEKEY, S("reversekey") },
|
||||
{ MDB_DUPSORT, S("dupsort") },
|
||||
{ MDB_INTEGERKEY, S("integerkey") },
|
||||
{ MDB_DUPFIXED, S("dupfixed") },
|
||||
{ MDB_INTEGERDUP, S("integerdup") },
|
||||
{ MDB_REVERSEDUP, S("reversedup") },
|
||||
{ 0, NULL, 0 }
|
||||
};
|
||||
|
||||
static void readhdr(void)
|
||||
{
|
||||
char *ptr;
|
||||
|
||||
while (fgets(dbuf.mv_data, dbuf.mv_size, stdin) != NULL) {
|
||||
lineno++;
|
||||
if (!strncmp(dbuf.mv_data, "VERSION=", STRLENOF("VERSION="))) {
|
||||
version=atoi((char *)dbuf.mv_data+STRLENOF("VERSION="));
|
||||
if (version > 3) {
|
||||
fprintf(stderr, "%s: line %" Z "d: unsupported VERSION %d\n",
|
||||
prog, lineno, version);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else if (!strncmp(dbuf.mv_data, "HEADER=END", STRLENOF("HEADER=END"))) {
|
||||
break;
|
||||
} else if (!strncmp(dbuf.mv_data, "format=", STRLENOF("format="))) {
|
||||
if (!strncmp((char *)dbuf.mv_data+STRLENOF("FORMAT="), "print", STRLENOF("print")))
|
||||
mode |= PRINT;
|
||||
else if (strncmp((char *)dbuf.mv_data+STRLENOF("FORMAT="), "bytevalue", STRLENOF("bytevalue"))) {
|
||||
fprintf(stderr, "%s: line %" Z "d: unsupported FORMAT %s\n",
|
||||
prog, lineno, (char *)dbuf.mv_data+STRLENOF("FORMAT="));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else if (!strncmp(dbuf.mv_data, "database=", STRLENOF("database="))) {
|
||||
ptr = memchr(dbuf.mv_data, '\n', dbuf.mv_size);
|
||||
if (ptr) *ptr = '\0';
|
||||
if (subname) free(subname);
|
||||
subname = strdup((char *)dbuf.mv_data+STRLENOF("database="));
|
||||
} else if (!strncmp(dbuf.mv_data, "type=", STRLENOF("type="))) {
|
||||
if (strncmp((char *)dbuf.mv_data+STRLENOF("type="), "btree", STRLENOF("btree"))) {
|
||||
fprintf(stderr, "%s: line %" Z "d: unsupported type %s\n",
|
||||
prog, lineno, (char *)dbuf.mv_data+STRLENOF("type="));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else if (!strncmp(dbuf.mv_data, "mapaddr=", STRLENOF("mapaddr="))) {
|
||||
int i;
|
||||
ptr = memchr(dbuf.mv_data, '\n', dbuf.mv_size);
|
||||
if (ptr) *ptr = '\0';
|
||||
i = sscanf((char *)dbuf.mv_data+STRLENOF("mapaddr="), "%p", &info.me_mapaddr);
|
||||
if (i != 1) {
|
||||
fprintf(stderr, "%s: line %" Z "d: invalid mapaddr %s\n",
|
||||
prog, lineno, (char *)dbuf.mv_data+STRLENOF("mapaddr="));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else if (!strncmp(dbuf.mv_data, "mapsize=", STRLENOF("mapsize="))) {
|
||||
int i;
|
||||
ptr = memchr(dbuf.mv_data, '\n', dbuf.mv_size);
|
||||
if (ptr) *ptr = '\0';
|
||||
i = sscanf((char *)dbuf.mv_data+STRLENOF("mapsize="), "%" Z "u", &info.me_mapsize);
|
||||
if (i != 1) {
|
||||
fprintf(stderr, "%s: line %" Z "d: invalid mapsize %s\n",
|
||||
prog, lineno, (char *)dbuf.mv_data+STRLENOF("mapsize="));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else if (!strncmp(dbuf.mv_data, "maxreaders=", STRLENOF("maxreaders="))) {
|
||||
int i;
|
||||
ptr = memchr(dbuf.mv_data, '\n', dbuf.mv_size);
|
||||
if (ptr) *ptr = '\0';
|
||||
i = sscanf((char *)dbuf.mv_data+STRLENOF("maxreaders="), "%u", &info.me_maxreaders);
|
||||
if (i != 1) {
|
||||
fprintf(stderr, "%s: line %" Z "d: invalid maxreaders %s\n",
|
||||
prog, lineno, (char *)dbuf.mv_data+STRLENOF("maxreaders="));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else {
|
||||
int i;
|
||||
for (i=0; dbflags[i].bit; i++) {
|
||||
if (!strncmp(dbuf.mv_data, dbflags[i].name, dbflags[i].len) &&
|
||||
((char *)dbuf.mv_data)[dbflags[i].len] == '=') {
|
||||
flags |= dbflags[i].bit;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!dbflags[i].bit) {
|
||||
ptr = memchr(dbuf.mv_data, '=', dbuf.mv_size);
|
||||
if (!ptr) {
|
||||
fprintf(stderr, "%s: line %" Z "d: unexpected format\n",
|
||||
prog, lineno);
|
||||
exit(EXIT_FAILURE);
|
||||
} else {
|
||||
*ptr = '\0';
|
||||
fprintf(stderr, "%s: line %" Z "d: unrecognized keyword ignored: %s\n",
|
||||
prog, lineno, (char *)dbuf.mv_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void badend(void)
|
||||
{
|
||||
fprintf(stderr, "%s: line %" Z "d: unexpected end of input\n",
|
||||
prog, lineno);
|
||||
}
|
||||
|
||||
static int unhex(unsigned char *c2)
|
||||
{
|
||||
int x, c;
|
||||
x = *c2++ & 0x4f;
|
||||
if (x & 0x40)
|
||||
x -= 55;
|
||||
c = x << 4;
|
||||
x = *c2 & 0x4f;
|
||||
if (x & 0x40)
|
||||
x -= 55;
|
||||
c |= x;
|
||||
return c;
|
||||
}
|
||||
|
||||
static int readline(MDB_val *out, MDB_val *buf)
|
||||
{
|
||||
unsigned char *c1, *c2, *end;
|
||||
size_t len;
|
||||
int c;
|
||||
|
||||
if (!(mode & NOHDR)) {
|
||||
c = fgetc(stdin);
|
||||
if (c == EOF) {
|
||||
Eof = 1;
|
||||
return EOF;
|
||||
}
|
||||
if (c != ' ') {
|
||||
lineno++;
|
||||
if (fgets(buf->mv_data, buf->mv_size, stdin) == NULL) {
|
||||
badend:
|
||||
Eof = 1;
|
||||
badend();
|
||||
return EOF;
|
||||
}
|
||||
if (c == 'D' && !strncmp(buf->mv_data, "ATA=END", STRLENOF("ATA=END")))
|
||||
return EOF;
|
||||
goto badend;
|
||||
}
|
||||
}
|
||||
if (fgets(buf->mv_data, buf->mv_size, stdin) == NULL) {
|
||||
Eof = 1;
|
||||
return EOF;
|
||||
}
|
||||
lineno++;
|
||||
|
||||
c1 = buf->mv_data;
|
||||
len = strlen((char *)c1);
|
||||
|
||||
/* Is buffer too short? */
|
||||
while (c1[len-1] != '\n') {
|
||||
buf->mv_data = realloc(buf->mv_data, buf->mv_size*2);
|
||||
if (!buf->mv_data) {
|
||||
Eof = 1;
|
||||
fprintf(stderr, "%s: line %" Z "d: out of memory, line too long\n",
|
||||
prog, lineno);
|
||||
return EOF;
|
||||
}
|
||||
c1 = buf->mv_data;
|
||||
c1 += buf->mv_size;
|
||||
if (fgets((char *)c1, buf->mv_size, stdin) == NULL) {
|
||||
Eof = 1;
|
||||
badend();
|
||||
return EOF;
|
||||
}
|
||||
buf->mv_size *= 2;
|
||||
len = strlen((char *)c1);
|
||||
}
|
||||
c1 = c2 = buf->mv_data;
|
||||
len = strlen((char *)c1);
|
||||
c1[--len] = '\0';
|
||||
end = c1 + len;
|
||||
|
||||
if (mode & PRINT) {
|
||||
while (c2 < end) {
|
||||
if (*c2 == '\\') {
|
||||
if (c2[1] == '\\') {
|
||||
c1++; c2 += 2;
|
||||
} else {
|
||||
if (c2+3 > end || !isxdigit(c2[1]) || !isxdigit(c2[2])) {
|
||||
Eof = 1;
|
||||
badend();
|
||||
return EOF;
|
||||
}
|
||||
*c1++ = unhex(++c2);
|
||||
c2 += 2;
|
||||
}
|
||||
} else {
|
||||
c1++; c2++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* odd length not allowed */
|
||||
if (len & 1) {
|
||||
Eof = 1;
|
||||
badend();
|
||||
return EOF;
|
||||
}
|
||||
while (c2 < end) {
|
||||
if (!isxdigit(*c2) || !isxdigit(c2[1])) {
|
||||
Eof = 1;
|
||||
badend();
|
||||
return EOF;
|
||||
}
|
||||
*c1++ = unhex(c2);
|
||||
c2 += 2;
|
||||
}
|
||||
}
|
||||
c2 = out->mv_data = buf->mv_data;
|
||||
out->mv_size = c1 - c2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usage(void)
|
||||
{
|
||||
fprintf(stderr, "usage: %s dbpath [-V] [-f input] [-n] [-s name] [-N] [-T]\n", prog);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int i, rc;
|
||||
MDB_env *env;
|
||||
MDB_txn *txn;
|
||||
MDB_cursor *mc;
|
||||
MDB_dbi dbi;
|
||||
char *envname;
|
||||
int envflags = 0, putflags = 0;
|
||||
int dohdr = 0;
|
||||
|
||||
prog = argv[0];
|
||||
|
||||
if (argc < 2) {
|
||||
usage();
|
||||
}
|
||||
|
||||
/* -f: load file instead of stdin
|
||||
* -n: use NOSUBDIR flag on env_open
|
||||
* -s: load into named subDB
|
||||
* -N: use NOOVERWRITE on puts
|
||||
* -T: read plaintext
|
||||
* -V: print version and exit
|
||||
*/
|
||||
while ((i = getopt(argc, argv, "f:ns:NTV")) != EOF) {
|
||||
switch(i) {
|
||||
case 'V':
|
||||
printf("%s\n", MDB_VERSION_STRING);
|
||||
exit(0);
|
||||
break;
|
||||
case 'f':
|
||||
if (freopen(optarg, "r", stdin) == NULL) {
|
||||
fprintf(stderr, "%s: %s: reopen: %s\n",
|
||||
prog, optarg, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
case 'n':
|
||||
envflags |= MDB_NOSUBDIR;
|
||||
break;
|
||||
case 's':
|
||||
subname = strdup(optarg);
|
||||
break;
|
||||
case 'N':
|
||||
putflags = MDB_NOOVERWRITE|MDB_NODUPDATA;
|
||||
break;
|
||||
case 'T':
|
||||
mode |= NOHDR;
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
}
|
||||
|
||||
if (optind != argc - 1)
|
||||
usage();
|
||||
|
||||
dbuf.mv_size = 4096;
|
||||
dbuf.mv_data = malloc(dbuf.mv_size);
|
||||
|
||||
if (!(mode & NOHDR))
|
||||
readhdr();
|
||||
|
||||
envname = argv[optind];
|
||||
rc = mdb_env_create(&env);
|
||||
if (rc) {
|
||||
fprintf(stderr, "mdb_env_create failed, error %d %s\n", rc, mdb_strerror(rc));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
mdb_env_set_maxdbs(env, 2);
|
||||
|
||||
if (info.me_maxreaders)
|
||||
mdb_env_set_maxreaders(env, info.me_maxreaders);
|
||||
|
||||
if (info.me_mapsize)
|
||||
mdb_env_set_mapsize(env, info.me_mapsize);
|
||||
|
||||
if (info.me_mapaddr)
|
||||
envflags |= MDB_FIXEDMAP;
|
||||
|
||||
rc = mdb_env_open(env, envname, envflags, 0664);
|
||||
if (rc) {
|
||||
fprintf(stderr, "mdb_env_open failed, error %d %s\n", rc, mdb_strerror(rc));
|
||||
goto env_close;
|
||||
}
|
||||
|
||||
kbuf.mv_size = mdb_env_get_maxkeysize(env) * 2 + 2;
|
||||
kbuf.mv_data = malloc(kbuf.mv_size);
|
||||
|
||||
while(!Eof) {
|
||||
MDB_val key, data;
|
||||
int batch = 0;
|
||||
flags = 0;
|
||||
|
||||
if (!dohdr) {
|
||||
dohdr = 1;
|
||||
} else if (!(mode & NOHDR))
|
||||
readhdr();
|
||||
|
||||
rc = mdb_txn_begin(env, NULL, 0, &txn);
|
||||
if (rc) {
|
||||
fprintf(stderr, "mdb_txn_begin failed, error %d %s\n", rc, mdb_strerror(rc));
|
||||
goto env_close;
|
||||
}
|
||||
|
||||
rc = mdb_open(txn, subname, flags|MDB_CREATE, &dbi);
|
||||
if (rc) {
|
||||
fprintf(stderr, "mdb_open failed, error %d %s\n", rc, mdb_strerror(rc));
|
||||
goto txn_abort;
|
||||
}
|
||||
|
||||
rc = mdb_cursor_open(txn, dbi, &mc);
|
||||
if (rc) {
|
||||
fprintf(stderr, "mdb_cursor_open failed, error %d %s\n", rc, mdb_strerror(rc));
|
||||
goto txn_abort;
|
||||
}
|
||||
|
||||
while(1) {
|
||||
rc = readline(&key, &kbuf);
|
||||
if (rc == EOF)
|
||||
break;
|
||||
if (rc)
|
||||
goto txn_abort;
|
||||
|
||||
rc = readline(&data, &dbuf);
|
||||
if (rc)
|
||||
goto txn_abort;
|
||||
|
||||
rc = mdb_cursor_put(mc, &key, &data, putflags);
|
||||
if (rc == MDB_KEYEXIST && putflags)
|
||||
continue;
|
||||
if (rc)
|
||||
goto txn_abort;
|
||||
batch++;
|
||||
if (batch == 100) {
|
||||
rc = mdb_txn_commit(txn);
|
||||
if (rc) {
|
||||
fprintf(stderr, "%s: line %" Z "d: txn_commit: %s\n",
|
||||
prog, lineno, mdb_strerror(rc));
|
||||
goto env_close;
|
||||
}
|
||||
rc = mdb_txn_begin(env, NULL, 0, &txn);
|
||||
if (rc) {
|
||||
fprintf(stderr, "mdb_txn_begin failed, error %d %s\n", rc, mdb_strerror(rc));
|
||||
goto env_close;
|
||||
}
|
||||
rc = mdb_cursor_open(txn, dbi, &mc);
|
||||
if (rc) {
|
||||
fprintf(stderr, "mdb_cursor_open failed, error %d %s\n", rc, mdb_strerror(rc));
|
||||
goto txn_abort;
|
||||
}
|
||||
batch = 0;
|
||||
}
|
||||
}
|
||||
rc = mdb_txn_commit(txn);
|
||||
txn = NULL;
|
||||
if (rc) {
|
||||
fprintf(stderr, "%s: line %" Z "d: txn_commit: %s\n",
|
||||
prog, lineno, mdb_strerror(rc));
|
||||
goto env_close;
|
||||
}
|
||||
mdb_dbi_close(env, dbi);
|
||||
}
|
||||
|
||||
txn_abort:
|
||||
mdb_txn_abort(txn);
|
||||
env_close:
|
||||
mdb_env_close(env);
|
||||
|
||||
return rc ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
||||
64
submodules/lmdb/libraries/liblmdb/mdb_stat.1
Normal file
64
submodules/lmdb/libraries/liblmdb/mdb_stat.1
Normal file
@@ -0,0 +1,64 @@
|
||||
.TH MDB_STAT 1 "2014/06/20" "LMDB 0.9.14"
|
||||
.\" Copyright 2012-2014 Howard Chu, Symas Corp. All Rights Reserved.
|
||||
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
|
||||
.SH NAME
|
||||
mdb_stat \- LMDB environment status tool
|
||||
.SH SYNOPSIS
|
||||
.B mdb_stat
|
||||
.BR \ envpath
|
||||
[\c
|
||||
.BR \-V ]
|
||||
[\c
|
||||
.BR \-e ]
|
||||
[\c
|
||||
.BR \-f [ f [ f ]]]
|
||||
[\c
|
||||
.BR \-n ]
|
||||
[\c
|
||||
.BR \-r [ r ]]
|
||||
[\c
|
||||
.BR \-a \ |
|
||||
.BI \-s \ subdb\fR]
|
||||
.SH DESCRIPTION
|
||||
The
|
||||
.B mdb_stat
|
||||
utility displays the status of an LMDB environment.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.BR \-V
|
||||
Write the library version number to the standard output, and exit.
|
||||
.TP
|
||||
.BR \-e
|
||||
Display information about the database environment.
|
||||
.TP
|
||||
.BR \-f
|
||||
Display information about the environment freelist.
|
||||
If \fB\-ff\fP is given, summarize each freelist entry.
|
||||
If \fB\-fff\fP is given, display the full list of page IDs in the freelist.
|
||||
.TP
|
||||
.BR \-n
|
||||
Display the status of an LMDB database which does not use subdirectories.
|
||||
.TP
|
||||
.BR \-r
|
||||
Display information about the environment reader table.
|
||||
Shows the process ID, thread ID, and transaction ID for each active
|
||||
reader slot. The process ID and transaction ID are in decimal, the
|
||||
thread ID is in hexadecimal. The transaction ID is displayed as "-"
|
||||
if the reader does not currently have a read transaction open.
|
||||
If \fB\-rr\fP is given, check for stale entries in the reader
|
||||
table and clear them. The reader table will be printed again
|
||||
after the check is performed.
|
||||
.TP
|
||||
.BR \-a
|
||||
Display the status of all of the subdatabases in the environment.
|
||||
.TP
|
||||
.BR \-s \ subdb
|
||||
Display the status of a specific subdatabase.
|
||||
.SH DIAGNOSTICS
|
||||
Exit status is zero if no errors occur.
|
||||
Errors result in a non-zero exit status and
|
||||
a diagnostic message being written to standard error.
|
||||
.SH "SEE ALSO"
|
||||
.BR mdb_copy (1)
|
||||
.SH AUTHOR
|
||||
Howard Chu of Symas Corporation <http://www.symas.com>
|
||||
263
submodules/lmdb/libraries/liblmdb/mdb_stat.c
Normal file
263
submodules/lmdb/libraries/liblmdb/mdb_stat.c
Normal file
@@ -0,0 +1,263 @@
|
||||
/* mdb_stat.c - memory-mapped database status tool */
|
||||
/*
|
||||
* Copyright 2011-2013 Howard Chu, Symas Corp.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted only as authorized by the OpenLDAP
|
||||
* Public License.
|
||||
*
|
||||
* A copy of this license is available in the file LICENSE in the
|
||||
* top-level directory of the distribution or, alternatively, at
|
||||
* <http://www.OpenLDAP.org/license.html>.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include "lmdb.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define Z "I"
|
||||
#else
|
||||
#define Z "z"
|
||||
#endif
|
||||
|
||||
static void prstat(MDB_stat *ms)
|
||||
{
|
||||
#if 0
|
||||
printf(" Page size: %u\n", ms->ms_psize);
|
||||
#endif
|
||||
printf(" Tree depth: %u\n", ms->ms_depth);
|
||||
printf(" Branch pages: %"Z"u\n", ms->ms_branch_pages);
|
||||
printf(" Leaf pages: %"Z"u\n", ms->ms_leaf_pages);
|
||||
printf(" Overflow pages: %"Z"u\n", ms->ms_overflow_pages);
|
||||
printf(" Entries: %"Z"u\n", ms->ms_entries);
|
||||
}
|
||||
|
||||
static void usage(char *prog)
|
||||
{
|
||||
fprintf(stderr, "usage: %s dbpath [-V] [-n] [-e] [-r[r]] [-f[f[f]]] [-a|-s subdb]\n", prog);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int i, rc;
|
||||
MDB_env *env;
|
||||
MDB_txn *txn;
|
||||
MDB_dbi dbi;
|
||||
MDB_stat mst;
|
||||
MDB_envinfo mei;
|
||||
char *prog = argv[0];
|
||||
char *envname;
|
||||
char *subname = NULL;
|
||||
int alldbs = 0, envinfo = 0, envflags = 0, freinfo = 0, rdrinfo = 0;
|
||||
|
||||
if (argc < 2) {
|
||||
usage(prog);
|
||||
}
|
||||
|
||||
/* -a: print stat of main DB and all subDBs
|
||||
* -s: print stat of only the named subDB
|
||||
* -e: print env info
|
||||
* -f: print freelist info
|
||||
* -r: print reader info
|
||||
* -n: use NOSUBDIR flag on env_open
|
||||
* -V: print version and exit
|
||||
* (default) print stat of only the main DB
|
||||
*/
|
||||
while ((i = getopt(argc, argv, "Vaefnrs:")) != EOF) {
|
||||
switch(i) {
|
||||
case 'V':
|
||||
printf("%s\n", MDB_VERSION_STRING);
|
||||
exit(0);
|
||||
break;
|
||||
case 'a':
|
||||
if (subname)
|
||||
usage(prog);
|
||||
alldbs++;
|
||||
break;
|
||||
case 'e':
|
||||
envinfo++;
|
||||
break;
|
||||
case 'f':
|
||||
freinfo++;
|
||||
break;
|
||||
case 'n':
|
||||
envflags |= MDB_NOSUBDIR;
|
||||
break;
|
||||
case 'r':
|
||||
rdrinfo++;
|
||||
break;
|
||||
case 's':
|
||||
if (alldbs)
|
||||
usage(prog);
|
||||
subname = optarg;
|
||||
break;
|
||||
default:
|
||||
usage(prog);
|
||||
}
|
||||
}
|
||||
|
||||
if (optind != argc - 1)
|
||||
usage(prog);
|
||||
|
||||
envname = argv[optind];
|
||||
rc = mdb_env_create(&env);
|
||||
if (rc) {
|
||||
fprintf(stderr, "mdb_env_create failed, error %d %s\n", rc, mdb_strerror(rc));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (alldbs || subname) {
|
||||
mdb_env_set_maxdbs(env, 4);
|
||||
}
|
||||
|
||||
rc = mdb_env_open(env, envname, envflags | MDB_RDONLY, 0664);
|
||||
if (rc) {
|
||||
fprintf(stderr, "mdb_env_open failed, error %d %s\n", rc, mdb_strerror(rc));
|
||||
goto env_close;
|
||||
}
|
||||
|
||||
if (envinfo) {
|
||||
(void)mdb_env_stat(env, &mst);
|
||||
(void)mdb_env_info(env, &mei);
|
||||
printf("Environment Info\n");
|
||||
printf(" Map address: %p\n", mei.me_mapaddr);
|
||||
printf(" Map size: %"Z"u\n", mei.me_mapsize);
|
||||
printf(" Page size: %u\n", mst.ms_psize);
|
||||
printf(" Max pages: %"Z"u\n", mei.me_mapsize / mst.ms_psize);
|
||||
printf(" Number of pages used: %"Z"u\n", mei.me_last_pgno+1);
|
||||
printf(" Last transaction ID: %"Z"u\n", mei.me_last_txnid);
|
||||
printf(" Max readers: %u\n", mei.me_maxreaders);
|
||||
printf(" Number of readers used: %u\n", mei.me_numreaders);
|
||||
}
|
||||
|
||||
if (rdrinfo) {
|
||||
printf("Reader Table Status\n");
|
||||
rc = mdb_reader_list(env, (MDB_msg_func *)fputs, stdout);
|
||||
if (rdrinfo > 1) {
|
||||
int dead;
|
||||
mdb_reader_check(env, &dead);
|
||||
printf(" %d stale readers cleared.\n", dead);
|
||||
rc = mdb_reader_list(env, (MDB_msg_func *)fputs, stdout);
|
||||
}
|
||||
if (!(subname || alldbs || freinfo))
|
||||
goto env_close;
|
||||
}
|
||||
|
||||
rc = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn);
|
||||
if (rc) {
|
||||
fprintf(stderr, "mdb_txn_begin failed, error %d %s\n", rc, mdb_strerror(rc));
|
||||
goto env_close;
|
||||
}
|
||||
|
||||
if (freinfo) {
|
||||
MDB_cursor *cursor;
|
||||
MDB_val key, data;
|
||||
size_t pages = 0, *iptr;
|
||||
|
||||
printf("Freelist Status\n");
|
||||
dbi = 0;
|
||||
rc = mdb_cursor_open(txn, dbi, &cursor);
|
||||
if (rc) {
|
||||
fprintf(stderr, "mdb_cursor_open failed, error %d %s\n", rc, mdb_strerror(rc));
|
||||
goto txn_abort;
|
||||
}
|
||||
rc = mdb_stat(txn, dbi, &mst);
|
||||
if (rc) {
|
||||
fprintf(stderr, "mdb_stat failed, error %d %s\n", rc, mdb_strerror(rc));
|
||||
goto txn_abort;
|
||||
}
|
||||
prstat(&mst);
|
||||
while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) {
|
||||
iptr = data.mv_data;
|
||||
pages += *iptr;
|
||||
if (freinfo > 1) {
|
||||
char *bad = "";
|
||||
size_t pg, prev;
|
||||
ssize_t i, j, span = 0;
|
||||
j = *iptr++;
|
||||
for (i = j, prev = 1; --i >= 0; ) {
|
||||
pg = iptr[i];
|
||||
if (pg <= prev)
|
||||
bad = " [bad sequence]";
|
||||
prev = pg;
|
||||
pg += span;
|
||||
for (; i >= span && iptr[i-span] == pg; span++, pg++) ;
|
||||
}
|
||||
printf(" Transaction %"Z"u, %"Z"d pages, maxspan %"Z"d%s\n",
|
||||
*(size_t *)key.mv_data, j, span, bad);
|
||||
if (freinfo > 2) {
|
||||
for (--j; j >= 0; ) {
|
||||
pg = iptr[j];
|
||||
for (span=1; --j >= 0 && iptr[j] == pg+span; span++) ;
|
||||
printf(span>1 ? " %9"Z"u[%"Z"d]\n" : " %9"Z"u\n",
|
||||
pg, span);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mdb_cursor_close(cursor);
|
||||
printf(" Free pages: %"Z"u\n", pages);
|
||||
}
|
||||
|
||||
rc = mdb_open(txn, subname, 0, &dbi);
|
||||
if (rc) {
|
||||
fprintf(stderr, "mdb_open failed, error %d %s\n", rc, mdb_strerror(rc));
|
||||
goto txn_abort;
|
||||
}
|
||||
|
||||
rc = mdb_stat(txn, dbi, &mst);
|
||||
if (rc) {
|
||||
fprintf(stderr, "mdb_stat failed, error %d %s\n", rc, mdb_strerror(rc));
|
||||
goto txn_abort;
|
||||
}
|
||||
printf("Status of %s\n", subname ? subname : "Main DB");
|
||||
prstat(&mst);
|
||||
|
||||
if (alldbs) {
|
||||
MDB_cursor *cursor;
|
||||
MDB_val key;
|
||||
|
||||
rc = mdb_cursor_open(txn, dbi, &cursor);
|
||||
if (rc) {
|
||||
fprintf(stderr, "mdb_cursor_open failed, error %d %s\n", rc, mdb_strerror(rc));
|
||||
goto txn_abort;
|
||||
}
|
||||
while ((rc = mdb_cursor_get(cursor, &key, NULL, MDB_NEXT_NODUP)) == 0) {
|
||||
char *str;
|
||||
MDB_dbi db2;
|
||||
if (memchr(key.mv_data, '\0', key.mv_size))
|
||||
continue;
|
||||
str = malloc(key.mv_size+1);
|
||||
memcpy(str, key.mv_data, key.mv_size);
|
||||
str[key.mv_size] = '\0';
|
||||
rc = mdb_open(txn, str, 0, &db2);
|
||||
if (rc == MDB_SUCCESS)
|
||||
printf("Status of %s\n", str);
|
||||
free(str);
|
||||
if (rc) continue;
|
||||
rc = mdb_stat(txn, db2, &mst);
|
||||
if (rc) {
|
||||
fprintf(stderr, "mdb_stat failed, error %d %s\n", rc, mdb_strerror(rc));
|
||||
goto txn_abort;
|
||||
}
|
||||
prstat(&mst);
|
||||
mdb_close(env, db2);
|
||||
}
|
||||
mdb_cursor_close(cursor);
|
||||
}
|
||||
|
||||
if (rc == MDB_NOTFOUND)
|
||||
rc = MDB_SUCCESS;
|
||||
|
||||
mdb_close(env, dbi);
|
||||
txn_abort:
|
||||
mdb_txn_abort(txn);
|
||||
env_close:
|
||||
mdb_env_close(env);
|
||||
|
||||
return rc ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
||||
360
submodules/lmdb/libraries/liblmdb/midl.c
Normal file
360
submodules/lmdb/libraries/liblmdb/midl.c
Normal file
@@ -0,0 +1,360 @@
|
||||
/** @file midl.c
|
||||
* @brief ldap bdb back-end ID List functions */
|
||||
/* $OpenLDAP$ */
|
||||
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
|
||||
*
|
||||
* Copyright 2000-2014 The OpenLDAP Foundation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted only as authorized by the OpenLDAP
|
||||
* Public License.
|
||||
*
|
||||
* A copy of this license is available in the file LICENSE in the
|
||||
* top-level directory of the distribution or, alternatively, at
|
||||
* <http://www.OpenLDAP.org/license.html>.
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include "midl.h"
|
||||
|
||||
/** @defgroup internal LMDB Internals
|
||||
* @{
|
||||
*/
|
||||
/** @defgroup idls ID List Management
|
||||
* @{
|
||||
*/
|
||||
#define CMP(x,y) ( (x) < (y) ? -1 : (x) > (y) )
|
||||
|
||||
unsigned mdb_midl_search( MDB_IDL ids, MDB_ID id )
|
||||
{
|
||||
/*
|
||||
* binary search of id in ids
|
||||
* if found, returns position of id
|
||||
* if not found, returns first position greater than id
|
||||
*/
|
||||
unsigned base = 0;
|
||||
unsigned cursor = 1;
|
||||
int val = 0;
|
||||
unsigned n = ids[0];
|
||||
|
||||
while( 0 < n ) {
|
||||
unsigned pivot = n >> 1;
|
||||
cursor = base + pivot + 1;
|
||||
val = CMP( ids[cursor], id );
|
||||
|
||||
if( val < 0 ) {
|
||||
n = pivot;
|
||||
|
||||
} else if ( val > 0 ) {
|
||||
base = cursor;
|
||||
n -= pivot + 1;
|
||||
|
||||
} else {
|
||||
return cursor;
|
||||
}
|
||||
}
|
||||
|
||||
if( val > 0 ) {
|
||||
++cursor;
|
||||
}
|
||||
return cursor;
|
||||
}
|
||||
|
||||
#if 0 /* superseded by append/sort */
|
||||
int mdb_midl_insert( MDB_IDL ids, MDB_ID id )
|
||||
{
|
||||
unsigned x, i;
|
||||
|
||||
x = mdb_midl_search( ids, id );
|
||||
assert( x > 0 );
|
||||
|
||||
if( x < 1 ) {
|
||||
/* internal error */
|
||||
return -2;
|
||||
}
|
||||
|
||||
if ( x <= ids[0] && ids[x] == id ) {
|
||||
/* duplicate */
|
||||
assert(0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ( ++ids[0] >= MDB_IDL_DB_MAX ) {
|
||||
/* no room */
|
||||
--ids[0];
|
||||
return -2;
|
||||
|
||||
} else {
|
||||
/* insert id */
|
||||
for (i=ids[0]; i>x; i--)
|
||||
ids[i] = ids[i-1];
|
||||
ids[x] = id;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
MDB_IDL mdb_midl_alloc(int num)
|
||||
{
|
||||
MDB_IDL ids = malloc((num+2) * sizeof(MDB_ID));
|
||||
if (ids) {
|
||||
*ids++ = num;
|
||||
*ids = 0;
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
void mdb_midl_free(MDB_IDL ids)
|
||||
{
|
||||
if (ids)
|
||||
free(ids-1);
|
||||
}
|
||||
|
||||
int mdb_midl_shrink( MDB_IDL *idp )
|
||||
{
|
||||
MDB_IDL ids = *idp;
|
||||
if (*(--ids) > MDB_IDL_UM_MAX &&
|
||||
(ids = realloc(ids, (MDB_IDL_UM_MAX+1) * sizeof(MDB_ID))))
|
||||
{
|
||||
*ids++ = MDB_IDL_UM_MAX;
|
||||
*idp = ids;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mdb_midl_grow( MDB_IDL *idp, int num )
|
||||
{
|
||||
MDB_IDL idn = *idp-1;
|
||||
/* grow it */
|
||||
idn = realloc(idn, (*idn + num + 2) * sizeof(MDB_ID));
|
||||
if (!idn)
|
||||
return ENOMEM;
|
||||
*idn++ += num;
|
||||
*idp = idn;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mdb_midl_need( MDB_IDL *idp, unsigned num )
|
||||
{
|
||||
MDB_IDL ids = *idp;
|
||||
num += ids[0];
|
||||
if (num > ids[-1]) {
|
||||
num = (num + num/4 + (256 + 2)) & -256;
|
||||
if (!(ids = realloc(ids-1, num * sizeof(MDB_ID))))
|
||||
return ENOMEM;
|
||||
*ids++ = num - 2;
|
||||
*idp = ids;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mdb_midl_append( MDB_IDL *idp, MDB_ID id )
|
||||
{
|
||||
MDB_IDL ids = *idp;
|
||||
/* Too big? */
|
||||
if (ids[0] >= ids[-1]) {
|
||||
if (mdb_midl_grow(idp, MDB_IDL_UM_MAX))
|
||||
return ENOMEM;
|
||||
ids = *idp;
|
||||
}
|
||||
ids[0]++;
|
||||
ids[ids[0]] = id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mdb_midl_append_list( MDB_IDL *idp, MDB_IDL app )
|
||||
{
|
||||
MDB_IDL ids = *idp;
|
||||
/* Too big? */
|
||||
if (ids[0] + app[0] >= ids[-1]) {
|
||||
if (mdb_midl_grow(idp, app[0]))
|
||||
return ENOMEM;
|
||||
ids = *idp;
|
||||
}
|
||||
memcpy(&ids[ids[0]+1], &app[1], app[0] * sizeof(MDB_ID));
|
||||
ids[0] += app[0];
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mdb_midl_append_range( MDB_IDL *idp, MDB_ID id, unsigned n )
|
||||
{
|
||||
MDB_ID *ids = *idp, len = ids[0];
|
||||
/* Too big? */
|
||||
if (len + n > ids[-1]) {
|
||||
if (mdb_midl_grow(idp, n | MDB_IDL_UM_MAX))
|
||||
return ENOMEM;
|
||||
ids = *idp;
|
||||
}
|
||||
ids[0] = len + n;
|
||||
ids += len;
|
||||
while (n)
|
||||
ids[n--] = id++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mdb_midl_xmerge( MDB_IDL idl, MDB_IDL merge )
|
||||
{
|
||||
MDB_ID old_id, merge_id, i = merge[0], j = idl[0], k = i+j, total = k;
|
||||
idl[0] = (MDB_ID)-1; /* delimiter for idl scan below */
|
||||
old_id = idl[j];
|
||||
while (i) {
|
||||
merge_id = merge[i--];
|
||||
for (; old_id < merge_id; old_id = idl[--j])
|
||||
idl[k--] = old_id;
|
||||
idl[k--] = merge_id;
|
||||
}
|
||||
idl[0] = total;
|
||||
}
|
||||
|
||||
/* Quicksort + Insertion sort for small arrays */
|
||||
|
||||
#define SMALL 8
|
||||
#define MIDL_SWAP(a,b) { itmp=(a); (a)=(b); (b)=itmp; }
|
||||
|
||||
void
|
||||
mdb_midl_sort( MDB_IDL ids )
|
||||
{
|
||||
/* Max possible depth of int-indexed tree * 2 items/level */
|
||||
int istack[sizeof(int)*CHAR_BIT * 2];
|
||||
int i,j,k,l,ir,jstack;
|
||||
MDB_ID a, itmp;
|
||||
|
||||
ir = (int)ids[0];
|
||||
l = 1;
|
||||
jstack = 0;
|
||||
for(;;) {
|
||||
if (ir - l < SMALL) { /* Insertion sort */
|
||||
for (j=l+1;j<=ir;j++) {
|
||||
a = ids[j];
|
||||
for (i=j-1;i>=1;i--) {
|
||||
if (ids[i] >= a) break;
|
||||
ids[i+1] = ids[i];
|
||||
}
|
||||
ids[i+1] = a;
|
||||
}
|
||||
if (jstack == 0) break;
|
||||
ir = istack[jstack--];
|
||||
l = istack[jstack--];
|
||||
} else {
|
||||
k = (l + ir) >> 1; /* Choose median of left, center, right */
|
||||
MIDL_SWAP(ids[k], ids[l+1]);
|
||||
if (ids[l] < ids[ir]) {
|
||||
MIDL_SWAP(ids[l], ids[ir]);
|
||||
}
|
||||
if (ids[l+1] < ids[ir]) {
|
||||
MIDL_SWAP(ids[l+1], ids[ir]);
|
||||
}
|
||||
if (ids[l] < ids[l+1]) {
|
||||
MIDL_SWAP(ids[l], ids[l+1]);
|
||||
}
|
||||
i = l+1;
|
||||
j = ir;
|
||||
a = ids[l+1];
|
||||
for(;;) {
|
||||
do i++; while(ids[i] > a);
|
||||
do j--; while(ids[j] < a);
|
||||
if (j < i) break;
|
||||
MIDL_SWAP(ids[i],ids[j]);
|
||||
}
|
||||
ids[l+1] = ids[j];
|
||||
ids[j] = a;
|
||||
jstack += 2;
|
||||
if (ir-i+1 >= j-l) {
|
||||
istack[jstack] = ir;
|
||||
istack[jstack-1] = i;
|
||||
ir = j-1;
|
||||
} else {
|
||||
istack[jstack] = j-1;
|
||||
istack[jstack-1] = l;
|
||||
l = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned mdb_mid2l_search( MDB_ID2L ids, MDB_ID id )
|
||||
{
|
||||
/*
|
||||
* binary search of id in ids
|
||||
* if found, returns position of id
|
||||
* if not found, returns first position greater than id
|
||||
*/
|
||||
unsigned base = 0;
|
||||
unsigned cursor = 1;
|
||||
int val = 0;
|
||||
unsigned n = (unsigned)ids[0].mid;
|
||||
|
||||
while( 0 < n ) {
|
||||
unsigned pivot = n >> 1;
|
||||
cursor = base + pivot + 1;
|
||||
val = CMP( id, ids[cursor].mid );
|
||||
|
||||
if( val < 0 ) {
|
||||
n = pivot;
|
||||
|
||||
} else if ( val > 0 ) {
|
||||
base = cursor;
|
||||
n -= pivot + 1;
|
||||
|
||||
} else {
|
||||
return cursor;
|
||||
}
|
||||
}
|
||||
|
||||
if( val > 0 ) {
|
||||
++cursor;
|
||||
}
|
||||
return cursor;
|
||||
}
|
||||
|
||||
int mdb_mid2l_insert( MDB_ID2L ids, MDB_ID2 *id )
|
||||
{
|
||||
unsigned x, i;
|
||||
|
||||
x = mdb_mid2l_search( ids, id->mid );
|
||||
|
||||
if( x < 1 ) {
|
||||
/* internal error */
|
||||
return -2;
|
||||
}
|
||||
|
||||
if ( x <= ids[0].mid && ids[x].mid == id->mid ) {
|
||||
/* duplicate */
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ( ids[0].mid >= MDB_IDL_UM_MAX ) {
|
||||
/* too big */
|
||||
return -2;
|
||||
|
||||
} else {
|
||||
/* insert id */
|
||||
ids[0].mid++;
|
||||
for (i=(unsigned)ids[0].mid; i>x; i--)
|
||||
ids[i] = ids[i-1];
|
||||
ids[x] = *id;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mdb_mid2l_append( MDB_ID2L ids, MDB_ID2 *id )
|
||||
{
|
||||
/* Too big? */
|
||||
if (ids[0].mid >= MDB_IDL_UM_MAX) {
|
||||
return -2;
|
||||
}
|
||||
ids[0].mid++;
|
||||
ids[ids[0].mid] = *id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @} */
|
||||
/** @} */
|
||||
186
submodules/lmdb/libraries/liblmdb/midl.h
Normal file
186
submodules/lmdb/libraries/liblmdb/midl.h
Normal file
@@ -0,0 +1,186 @@
|
||||
/** @file midl.h
|
||||
* @brief LMDB ID List header file.
|
||||
*
|
||||
* This file was originally part of back-bdb but has been
|
||||
* modified for use in libmdb. Most of the macros defined
|
||||
* in this file are unused, just left over from the original.
|
||||
*
|
||||
* This file is only used internally in libmdb and its definitions
|
||||
* are not exposed publicly.
|
||||
*/
|
||||
/* $OpenLDAP$ */
|
||||
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
|
||||
*
|
||||
* Copyright 2000-2014 The OpenLDAP Foundation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted only as authorized by the OpenLDAP
|
||||
* Public License.
|
||||
*
|
||||
* A copy of this license is available in the file LICENSE in the
|
||||
* top-level directory of the distribution or, alternatively, at
|
||||
* <http://www.OpenLDAP.org/license.html>.
|
||||
*/
|
||||
|
||||
#ifndef _MDB_MIDL_H_
|
||||
#define _MDB_MIDL_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @defgroup internal LMDB Internals
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** @defgroup idls ID List Management
|
||||
* @{
|
||||
*/
|
||||
/** A generic unsigned ID number. These were entryIDs in back-bdb.
|
||||
* Preferably it should have the same size as a pointer.
|
||||
*/
|
||||
typedef size_t MDB_ID;
|
||||
|
||||
/** An IDL is an ID List, a sorted array of IDs. The first
|
||||
* element of the array is a counter for how many actual
|
||||
* IDs are in the list. In the original back-bdb code, IDLs are
|
||||
* sorted in ascending order. For libmdb IDLs are sorted in
|
||||
* descending order.
|
||||
*/
|
||||
typedef MDB_ID *MDB_IDL;
|
||||
|
||||
/* IDL sizes - likely should be even bigger
|
||||
* limiting factors: sizeof(ID), thread stack size
|
||||
*/
|
||||
#define MDB_IDL_LOGN 16 /* DB_SIZE is 2^16, UM_SIZE is 2^17 */
|
||||
#define MDB_IDL_DB_SIZE (1<<MDB_IDL_LOGN)
|
||||
#define MDB_IDL_UM_SIZE (1<<(MDB_IDL_LOGN+1))
|
||||
|
||||
#define MDB_IDL_DB_MAX (MDB_IDL_DB_SIZE-1)
|
||||
#define MDB_IDL_UM_MAX (MDB_IDL_UM_SIZE-1)
|
||||
|
||||
#define MDB_IDL_SIZEOF(ids) (((ids)[0]+1) * sizeof(MDB_ID))
|
||||
#define MDB_IDL_IS_ZERO(ids) ( (ids)[0] == 0 )
|
||||
#define MDB_IDL_CPY( dst, src ) (memcpy( dst, src, MDB_IDL_SIZEOF( src ) ))
|
||||
#define MDB_IDL_FIRST( ids ) ( (ids)[1] )
|
||||
#define MDB_IDL_LAST( ids ) ( (ids)[(ids)[0]] )
|
||||
|
||||
/** Current max length of an #mdb_midl_alloc()ed IDL */
|
||||
#define MDB_IDL_ALLOCLEN( ids ) ( (ids)[-1] )
|
||||
|
||||
/** Append ID to IDL. The IDL must be big enough. */
|
||||
#define mdb_midl_xappend(idl, id) do { \
|
||||
MDB_ID *xidl = (idl), xlen = ++(xidl[0]); \
|
||||
xidl[xlen] = (id); \
|
||||
} while (0)
|
||||
|
||||
/** Search for an ID in an IDL.
|
||||
* @param[in] ids The IDL to search.
|
||||
* @param[in] id The ID to search for.
|
||||
* @return The index of the first ID greater than or equal to \b id.
|
||||
*/
|
||||
unsigned mdb_midl_search( MDB_IDL ids, MDB_ID id );
|
||||
|
||||
/** Allocate an IDL.
|
||||
* Allocates memory for an IDL of the given size.
|
||||
* @return IDL on success, NULL on failure.
|
||||
*/
|
||||
MDB_IDL mdb_midl_alloc(int num);
|
||||
|
||||
/** Free an IDL.
|
||||
* @param[in] ids The IDL to free.
|
||||
*/
|
||||
void mdb_midl_free(MDB_IDL ids);
|
||||
|
||||
/** Shrink an IDL.
|
||||
* Return the IDL to the default size if it has grown larger.
|
||||
* @param[in,out] idp Address of the IDL to shrink.
|
||||
* @return 0 on no change, non-zero if shrunk.
|
||||
*/
|
||||
int mdb_midl_shrink(MDB_IDL *idp);
|
||||
|
||||
/** Make room for num additional elements in an IDL.
|
||||
* @param[in,out] idp Address of the IDL.
|
||||
* @param[in] num Number of elements to make room for.
|
||||
* @return 0 on success, ENOMEM on failure.
|
||||
*/
|
||||
int mdb_midl_need(MDB_IDL *idp, unsigned num);
|
||||
|
||||
/** Append an ID onto an IDL.
|
||||
* @param[in,out] idp Address of the IDL to append to.
|
||||
* @param[in] id The ID to append.
|
||||
* @return 0 on success, ENOMEM if the IDL is too large.
|
||||
*/
|
||||
int mdb_midl_append( MDB_IDL *idp, MDB_ID id );
|
||||
|
||||
/** Append an IDL onto an IDL.
|
||||
* @param[in,out] idp Address of the IDL to append to.
|
||||
* @param[in] app The IDL to append.
|
||||
* @return 0 on success, ENOMEM if the IDL is too large.
|
||||
*/
|
||||
int mdb_midl_append_list( MDB_IDL *idp, MDB_IDL app );
|
||||
|
||||
/** Append an ID range onto an IDL.
|
||||
* @param[in,out] idp Address of the IDL to append to.
|
||||
* @param[in] id The lowest ID to append.
|
||||
* @param[in] n Number of IDs to append.
|
||||
* @return 0 on success, ENOMEM if the IDL is too large.
|
||||
*/
|
||||
int mdb_midl_append_range( MDB_IDL *idp, MDB_ID id, unsigned n );
|
||||
|
||||
/** Merge an IDL onto an IDL. The destination IDL must be big enough.
|
||||
* @param[in] idl The IDL to merge into.
|
||||
* @param[in] merge The IDL to merge.
|
||||
*/
|
||||
void mdb_midl_xmerge( MDB_IDL idl, MDB_IDL merge );
|
||||
|
||||
/** Sort an IDL.
|
||||
* @param[in,out] ids The IDL to sort.
|
||||
*/
|
||||
void mdb_midl_sort( MDB_IDL ids );
|
||||
|
||||
/** An ID2 is an ID/pointer pair.
|
||||
*/
|
||||
typedef struct MDB_ID2 {
|
||||
MDB_ID mid; /**< The ID */
|
||||
void *mptr; /**< The pointer */
|
||||
} MDB_ID2;
|
||||
|
||||
/** An ID2L is an ID2 List, a sorted array of ID2s.
|
||||
* The first element's \b mid member is a count of how many actual
|
||||
* elements are in the array. The \b mptr member of the first element is unused.
|
||||
* The array is sorted in ascending order by \b mid.
|
||||
*/
|
||||
typedef MDB_ID2 *MDB_ID2L;
|
||||
|
||||
/** Search for an ID in an ID2L.
|
||||
* @param[in] ids The ID2L to search.
|
||||
* @param[in] id The ID to search for.
|
||||
* @return The index of the first ID2 whose \b mid member is greater than or equal to \b id.
|
||||
*/
|
||||
unsigned mdb_mid2l_search( MDB_ID2L ids, MDB_ID id );
|
||||
|
||||
|
||||
/** Insert an ID2 into a ID2L.
|
||||
* @param[in,out] ids The ID2L to insert into.
|
||||
* @param[in] id The ID2 to insert.
|
||||
* @return 0 on success, -1 if the ID was already present in the ID2L.
|
||||
*/
|
||||
int mdb_mid2l_insert( MDB_ID2L ids, MDB_ID2 *id );
|
||||
|
||||
/** Append an ID2 into a ID2L.
|
||||
* @param[in,out] ids The ID2L to append into.
|
||||
* @param[in] id The ID2 to append.
|
||||
* @return 0 on success, -2 if the ID2L is too big.
|
||||
*/
|
||||
int mdb_mid2l_append( MDB_ID2L ids, MDB_ID2 *id );
|
||||
|
||||
/** @} */
|
||||
/** @} */
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* _MDB_MIDL_H_ */
|
||||
173
submodules/lmdb/libraries/liblmdb/mtest.c
Normal file
173
submodules/lmdb/libraries/liblmdb/mtest.c
Normal file
@@ -0,0 +1,173 @@
|
||||
/* mtest.c - memory-mapped database tester/toy */
|
||||
/*
|
||||
* Copyright 2011-2014 Howard Chu, Symas Corp.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted only as authorized by the OpenLDAP
|
||||
* Public License.
|
||||
*
|
||||
* A copy of this license is available in the file LICENSE in the
|
||||
* top-level directory of the distribution or, alternatively, at
|
||||
* <http://www.OpenLDAP.org/license.html>.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include "lmdb.h"
|
||||
|
||||
#define E(expr) CHECK((rc = (expr)) == MDB_SUCCESS, #expr)
|
||||
#define RES(err, expr) ((rc = expr) == (err) || (CHECK(!rc, #expr), 0))
|
||||
#define CHECK(test, msg) ((test) ? (void)0 : ((void)fprintf(stderr, \
|
||||
"%s:%d: %s: %s\n", __FILE__, __LINE__, msg, mdb_strerror(rc)), abort()))
|
||||
|
||||
int main(int argc,char * argv[])
|
||||
{
|
||||
int i = 0, j = 0, rc;
|
||||
MDB_env *env;
|
||||
MDB_dbi dbi;
|
||||
MDB_val key, data;
|
||||
MDB_txn *txn;
|
||||
MDB_stat mst;
|
||||
MDB_cursor *cursor, *cur2;
|
||||
MDB_cursor_op op;
|
||||
int count;
|
||||
int *values;
|
||||
char sval[32] = "";
|
||||
|
||||
srand(time(NULL));
|
||||
|
||||
count = (rand()%384) + 64;
|
||||
values = (int *)malloc(count*sizeof(int));
|
||||
|
||||
for(i = 0;i<count;i++) {
|
||||
values[i] = rand()%1024;
|
||||
}
|
||||
|
||||
E(mdb_env_create(&env));
|
||||
E(mdb_env_set_mapsize(env, 10485760));
|
||||
E(mdb_env_open(env, "./testdb", MDB_FIXEDMAP /*|MDB_NOSYNC*/, 0664));
|
||||
E(mdb_txn_begin(env, NULL, 0, &txn));
|
||||
E(mdb_open(txn, NULL, 0, &dbi));
|
||||
|
||||
key.mv_size = sizeof(int);
|
||||
key.mv_data = sval;
|
||||
data.mv_size = sizeof(sval);
|
||||
data.mv_data = sval;
|
||||
|
||||
printf("Adding %d values\n", count);
|
||||
for (i=0;i<count;i++) {
|
||||
sprintf(sval, "%03x %d foo bar", values[i], values[i]);
|
||||
if (RES(MDB_KEYEXIST, mdb_put(txn, dbi, &key, &data, MDB_NOOVERWRITE))) {
|
||||
j++;
|
||||
data.mv_size = sizeof(sval);
|
||||
data.mv_data = sval;
|
||||
}
|
||||
}
|
||||
if (j) printf("%d duplicates skipped\n", j);
|
||||
E(mdb_txn_commit(txn));
|
||||
E(mdb_env_stat(env, &mst));
|
||||
|
||||
E(mdb_txn_begin(env, NULL, 1, &txn));
|
||||
E(mdb_cursor_open(txn, dbi, &cursor));
|
||||
while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) {
|
||||
printf("key: %p %.*s, data: %p %.*s\n",
|
||||
key.mv_data, (int) key.mv_size, (char *) key.mv_data,
|
||||
data.mv_data, (int) data.mv_size, (char *) data.mv_data);
|
||||
}
|
||||
CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get");
|
||||
mdb_cursor_close(cursor);
|
||||
mdb_txn_abort(txn);
|
||||
|
||||
j=0;
|
||||
key.mv_data = sval;
|
||||
for (i= count - 1; i > -1; i-= (rand()%5)) {
|
||||
j++;
|
||||
txn=NULL;
|
||||
E(mdb_txn_begin(env, NULL, 0, &txn));
|
||||
sprintf(sval, "%03x ", values[i]);
|
||||
if (RES(MDB_NOTFOUND, mdb_del(txn, dbi, &key, NULL))) {
|
||||
j--;
|
||||
mdb_txn_abort(txn);
|
||||
} else {
|
||||
E(mdb_txn_commit(txn));
|
||||
}
|
||||
}
|
||||
free(values);
|
||||
printf("Deleted %d values\n", j);
|
||||
|
||||
E(mdb_env_stat(env, &mst));
|
||||
E(mdb_txn_begin(env, NULL, 1, &txn));
|
||||
E(mdb_cursor_open(txn, dbi, &cursor));
|
||||
printf("Cursor next\n");
|
||||
while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) {
|
||||
printf("key: %.*s, data: %.*s\n",
|
||||
(int) key.mv_size, (char *) key.mv_data,
|
||||
(int) data.mv_size, (char *) data.mv_data);
|
||||
}
|
||||
CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get");
|
||||
printf("Cursor last\n");
|
||||
E(mdb_cursor_get(cursor, &key, &data, MDB_LAST));
|
||||
printf("key: %.*s, data: %.*s\n",
|
||||
(int) key.mv_size, (char *) key.mv_data,
|
||||
(int) data.mv_size, (char *) data.mv_data);
|
||||
printf("Cursor prev\n");
|
||||
while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_PREV)) == 0) {
|
||||
printf("key: %.*s, data: %.*s\n",
|
||||
(int) key.mv_size, (char *) key.mv_data,
|
||||
(int) data.mv_size, (char *) data.mv_data);
|
||||
}
|
||||
CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get");
|
||||
printf("Cursor last/prev\n");
|
||||
E(mdb_cursor_get(cursor, &key, &data, MDB_LAST));
|
||||
printf("key: %.*s, data: %.*s\n",
|
||||
(int) key.mv_size, (char *) key.mv_data,
|
||||
(int) data.mv_size, (char *) data.mv_data);
|
||||
E(mdb_cursor_get(cursor, &key, &data, MDB_PREV));
|
||||
printf("key: %.*s, data: %.*s\n",
|
||||
(int) key.mv_size, (char *) key.mv_data,
|
||||
(int) data.mv_size, (char *) data.mv_data);
|
||||
|
||||
mdb_txn_abort(txn);
|
||||
|
||||
printf("Deleting with cursor\n");
|
||||
E(mdb_txn_begin(env, NULL, 0, &txn));
|
||||
E(mdb_cursor_open(txn, dbi, &cur2));
|
||||
for (i=0; i<50; i++) {
|
||||
if (RES(MDB_NOTFOUND, mdb_cursor_get(cur2, &key, &data, MDB_NEXT)))
|
||||
break;
|
||||
printf("key: %p %.*s, data: %p %.*s\n",
|
||||
key.mv_data, (int) key.mv_size, (char *) key.mv_data,
|
||||
data.mv_data, (int) data.mv_size, (char *) data.mv_data);
|
||||
E(mdb_del(txn, dbi, &key, NULL));
|
||||
}
|
||||
|
||||
printf("Restarting cursor in txn\n");
|
||||
for (op=MDB_FIRST, i=0; i<=32; op=MDB_NEXT, i++) {
|
||||
if (RES(MDB_NOTFOUND, mdb_cursor_get(cur2, &key, &data, op)))
|
||||
break;
|
||||
printf("key: %p %.*s, data: %p %.*s\n",
|
||||
key.mv_data, (int) key.mv_size, (char *) key.mv_data,
|
||||
data.mv_data, (int) data.mv_size, (char *) data.mv_data);
|
||||
}
|
||||
mdb_cursor_close(cur2);
|
||||
E(mdb_txn_commit(txn));
|
||||
|
||||
printf("Restarting cursor outside txn\n");
|
||||
E(mdb_txn_begin(env, NULL, 0, &txn));
|
||||
E(mdb_cursor_open(txn, dbi, &cursor));
|
||||
for (op=MDB_FIRST, i=0; i<=32; op=MDB_NEXT, i++) {
|
||||
if (RES(MDB_NOTFOUND, mdb_cursor_get(cursor, &key, &data, op)))
|
||||
break;
|
||||
printf("key: %p %.*s, data: %p %.*s\n",
|
||||
key.mv_data, (int) key.mv_size, (char *) key.mv_data,
|
||||
data.mv_data, (int) data.mv_size, (char *) data.mv_data);
|
||||
}
|
||||
mdb_cursor_close(cursor);
|
||||
mdb_close(env, dbi);
|
||||
|
||||
mdb_txn_abort(txn);
|
||||
mdb_env_close(env);
|
||||
|
||||
return 0;
|
||||
}
|
||||
123
submodules/lmdb/libraries/liblmdb/mtest2.c
Normal file
123
submodules/lmdb/libraries/liblmdb/mtest2.c
Normal file
@@ -0,0 +1,123 @@
|
||||
/* mtest2.c - memory-mapped database tester/toy */
|
||||
/*
|
||||
* Copyright 2011-2014 Howard Chu, Symas Corp.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted only as authorized by the OpenLDAP
|
||||
* Public License.
|
||||
*
|
||||
* A copy of this license is available in the file LICENSE in the
|
||||
* top-level directory of the distribution or, alternatively, at
|
||||
* <http://www.OpenLDAP.org/license.html>.
|
||||
*/
|
||||
|
||||
/* Just like mtest.c, but using a subDB instead of the main DB */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include "lmdb.h"
|
||||
|
||||
#define E(expr) CHECK((rc = (expr)) == MDB_SUCCESS, #expr)
|
||||
#define RES(err, expr) ((rc = expr) == (err) || (CHECK(!rc, #expr), 0))
|
||||
#define CHECK(test, msg) ((test) ? (void)0 : ((void)fprintf(stderr, \
|
||||
"%s:%d: %s: %s\n", __FILE__, __LINE__, msg, mdb_strerror(rc)), abort()))
|
||||
|
||||
int main(int argc,char * argv[])
|
||||
{
|
||||
int i = 0, j = 0, rc;
|
||||
MDB_env *env;
|
||||
MDB_dbi dbi;
|
||||
MDB_val key, data;
|
||||
MDB_txn *txn;
|
||||
MDB_stat mst;
|
||||
MDB_cursor *cursor;
|
||||
int count;
|
||||
int *values;
|
||||
char sval[32] = "";
|
||||
|
||||
srand(time(NULL));
|
||||
|
||||
count = (rand()%384) + 64;
|
||||
values = (int *)malloc(count*sizeof(int));
|
||||
|
||||
for(i = 0;i<count;i++) {
|
||||
values[i] = rand()%1024;
|
||||
}
|
||||
|
||||
E(mdb_env_create(&env));
|
||||
E(mdb_env_set_mapsize(env, 10485760));
|
||||
E(mdb_env_set_maxdbs(env, 4));
|
||||
E(mdb_env_open(env, "./testdb", MDB_FIXEDMAP|MDB_NOSYNC, 0664));
|
||||
E(mdb_txn_begin(env, NULL, 0, &txn));
|
||||
E(mdb_open(txn, "id1", MDB_CREATE, &dbi));
|
||||
|
||||
key.mv_size = sizeof(int);
|
||||
key.mv_data = sval;
|
||||
data.mv_size = sizeof(sval);
|
||||
data.mv_data = sval;
|
||||
|
||||
printf("Adding %d values\n", count);
|
||||
for (i=0;i<count;i++) {
|
||||
sprintf(sval, "%03x %d foo bar", values[i], values[i]);
|
||||
if (RES(MDB_KEYEXIST, mdb_put(txn, dbi, &key, &data, MDB_NOOVERWRITE)))
|
||||
j++;
|
||||
}
|
||||
if (j) printf("%d duplicates skipped\n", j);
|
||||
E(mdb_txn_commit(txn));
|
||||
E(mdb_env_stat(env, &mst));
|
||||
|
||||
E(mdb_txn_begin(env, NULL, 1, &txn));
|
||||
E(mdb_cursor_open(txn, dbi, &cursor));
|
||||
while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) {
|
||||
printf("key: %p %.*s, data: %p %.*s\n",
|
||||
key.mv_data, (int) key.mv_size, (char *) key.mv_data,
|
||||
data.mv_data, (int) data.mv_size, (char *) data.mv_data);
|
||||
}
|
||||
CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get");
|
||||
mdb_cursor_close(cursor);
|
||||
mdb_txn_abort(txn);
|
||||
|
||||
j=0;
|
||||
key.mv_data = sval;
|
||||
for (i= count - 1; i > -1; i-= (rand()%5)) {
|
||||
j++;
|
||||
txn=NULL;
|
||||
E(mdb_txn_begin(env, NULL, 0, &txn));
|
||||
sprintf(sval, "%03x ", values[i]);
|
||||
if (RES(MDB_NOTFOUND, mdb_del(txn, dbi, &key, NULL))) {
|
||||
j--;
|
||||
mdb_txn_abort(txn);
|
||||
} else {
|
||||
E(mdb_txn_commit(txn));
|
||||
}
|
||||
}
|
||||
free(values);
|
||||
printf("Deleted %d values\n", j);
|
||||
|
||||
E(mdb_env_stat(env, &mst));
|
||||
E(mdb_txn_begin(env, NULL, 1, &txn));
|
||||
E(mdb_cursor_open(txn, dbi, &cursor));
|
||||
printf("Cursor next\n");
|
||||
while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) {
|
||||
printf("key: %.*s, data: %.*s\n",
|
||||
(int) key.mv_size, (char *) key.mv_data,
|
||||
(int) data.mv_size, (char *) data.mv_data);
|
||||
}
|
||||
CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get");
|
||||
printf("Cursor prev\n");
|
||||
while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_PREV)) == 0) {
|
||||
printf("key: %.*s, data: %.*s\n",
|
||||
(int) key.mv_size, (char *) key.mv_data,
|
||||
(int) data.mv_size, (char *) data.mv_data);
|
||||
}
|
||||
CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get");
|
||||
mdb_cursor_close(cursor);
|
||||
mdb_close(env, dbi);
|
||||
|
||||
mdb_txn_abort(txn);
|
||||
mdb_env_close(env);
|
||||
|
||||
return 0;
|
||||
}
|
||||
133
submodules/lmdb/libraries/liblmdb/mtest3.c
Normal file
133
submodules/lmdb/libraries/liblmdb/mtest3.c
Normal file
@@ -0,0 +1,133 @@
|
||||
/* mtest3.c - memory-mapped database tester/toy */
|
||||
/*
|
||||
* Copyright 2011-2014 Howard Chu, Symas Corp.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted only as authorized by the OpenLDAP
|
||||
* Public License.
|
||||
*
|
||||
* A copy of this license is available in the file LICENSE in the
|
||||
* top-level directory of the distribution or, alternatively, at
|
||||
* <http://www.OpenLDAP.org/license.html>.
|
||||
*/
|
||||
|
||||
/* Tests for sorted duplicate DBs */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include "lmdb.h"
|
||||
|
||||
#define E(expr) CHECK((rc = (expr)) == MDB_SUCCESS, #expr)
|
||||
#define RES(err, expr) ((rc = expr) == (err) || (CHECK(!rc, #expr), 0))
|
||||
#define CHECK(test, msg) ((test) ? (void)0 : ((void)fprintf(stderr, \
|
||||
"%s:%d: %s: %s\n", __FILE__, __LINE__, msg, mdb_strerror(rc)), abort()))
|
||||
|
||||
int main(int argc,char * argv[])
|
||||
{
|
||||
int i = 0, j = 0, rc;
|
||||
MDB_env *env;
|
||||
MDB_dbi dbi;
|
||||
MDB_val key, data;
|
||||
MDB_txn *txn;
|
||||
MDB_stat mst;
|
||||
MDB_cursor *cursor;
|
||||
int count;
|
||||
int *values;
|
||||
char sval[32];
|
||||
char kval[sizeof(int)];
|
||||
|
||||
srand(time(NULL));
|
||||
|
||||
memset(sval, 0, sizeof(sval));
|
||||
|
||||
count = (rand()%384) + 64;
|
||||
values = (int *)malloc(count*sizeof(int));
|
||||
|
||||
for(i = 0;i<count;i++) {
|
||||
values[i] = rand()%1024;
|
||||
}
|
||||
|
||||
E(mdb_env_create(&env));
|
||||
E(mdb_env_set_mapsize(env, 10485760));
|
||||
E(mdb_env_set_maxdbs(env, 4));
|
||||
E(mdb_env_open(env, "./testdb", MDB_FIXEDMAP|MDB_NOSYNC, 0664));
|
||||
E(mdb_txn_begin(env, NULL, 0, &txn));
|
||||
E(mdb_open(txn, "id2", MDB_CREATE|MDB_DUPSORT, &dbi));
|
||||
|
||||
key.mv_size = sizeof(int);
|
||||
key.mv_data = kval;
|
||||
data.mv_size = sizeof(sval);
|
||||
data.mv_data = sval;
|
||||
|
||||
printf("Adding %d values\n", count);
|
||||
for (i=0;i<count;i++) {
|
||||
if (!(i & 0x0f))
|
||||
sprintf(kval, "%03x", values[i]);
|
||||
sprintf(sval, "%03x %d foo bar", values[i], values[i]);
|
||||
if (RES(MDB_KEYEXIST, mdb_put(txn, dbi, &key, &data, MDB_NODUPDATA)))
|
||||
j++;
|
||||
}
|
||||
if (j) printf("%d duplicates skipped\n", j);
|
||||
E(mdb_txn_commit(txn));
|
||||
E(mdb_env_stat(env, &mst));
|
||||
|
||||
E(mdb_txn_begin(env, NULL, 1, &txn));
|
||||
E(mdb_cursor_open(txn, dbi, &cursor));
|
||||
while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) {
|
||||
printf("key: %p %.*s, data: %p %.*s\n",
|
||||
key.mv_data, (int) key.mv_size, (char *) key.mv_data,
|
||||
data.mv_data, (int) data.mv_size, (char *) data.mv_data);
|
||||
}
|
||||
CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get");
|
||||
mdb_cursor_close(cursor);
|
||||
mdb_txn_abort(txn);
|
||||
|
||||
j=0;
|
||||
|
||||
for (i= count - 1; i > -1; i-= (rand()%5)) {
|
||||
j++;
|
||||
txn=NULL;
|
||||
E(mdb_txn_begin(env, NULL, 0, &txn));
|
||||
sprintf(kval, "%03x", values[i & ~0x0f]);
|
||||
sprintf(sval, "%03x %d foo bar", values[i], values[i]);
|
||||
key.mv_size = sizeof(int);
|
||||
key.mv_data = kval;
|
||||
data.mv_size = sizeof(sval);
|
||||
data.mv_data = sval;
|
||||
if (RES(MDB_NOTFOUND, mdb_del(txn, dbi, &key, &data))) {
|
||||
j--;
|
||||
mdb_txn_abort(txn);
|
||||
} else {
|
||||
E(mdb_txn_commit(txn));
|
||||
}
|
||||
}
|
||||
free(values);
|
||||
printf("Deleted %d values\n", j);
|
||||
|
||||
E(mdb_env_stat(env, &mst));
|
||||
E(mdb_txn_begin(env, NULL, 1, &txn));
|
||||
E(mdb_cursor_open(txn, dbi, &cursor));
|
||||
printf("Cursor next\n");
|
||||
while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) {
|
||||
printf("key: %.*s, data: %.*s\n",
|
||||
(int) key.mv_size, (char *) key.mv_data,
|
||||
(int) data.mv_size, (char *) data.mv_data);
|
||||
}
|
||||
CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get");
|
||||
printf("Cursor prev\n");
|
||||
while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_PREV)) == 0) {
|
||||
printf("key: %.*s, data: %.*s\n",
|
||||
(int) key.mv_size, (char *) key.mv_data,
|
||||
(int) data.mv_size, (char *) data.mv_data);
|
||||
}
|
||||
CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get");
|
||||
mdb_cursor_close(cursor);
|
||||
mdb_close(env, dbi);
|
||||
|
||||
mdb_txn_abort(txn);
|
||||
mdb_env_close(env);
|
||||
|
||||
return 0;
|
||||
}
|
||||
168
submodules/lmdb/libraries/liblmdb/mtest4.c
Normal file
168
submodules/lmdb/libraries/liblmdb/mtest4.c
Normal file
@@ -0,0 +1,168 @@
|
||||
/* mtest4.c - memory-mapped database tester/toy */
|
||||
/*
|
||||
* Copyright 2011-2014 Howard Chu, Symas Corp.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted only as authorized by the OpenLDAP
|
||||
* Public License.
|
||||
*
|
||||
* A copy of this license is available in the file LICENSE in the
|
||||
* top-level directory of the distribution or, alternatively, at
|
||||
* <http://www.OpenLDAP.org/license.html>.
|
||||
*/
|
||||
|
||||
/* Tests for sorted duplicate DBs with fixed-size keys */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include "lmdb.h"
|
||||
|
||||
#define E(expr) CHECK((rc = (expr)) == MDB_SUCCESS, #expr)
|
||||
#define RES(err, expr) ((rc = expr) == (err) || (CHECK(!rc, #expr), 0))
|
||||
#define CHECK(test, msg) ((test) ? (void)0 : ((void)fprintf(stderr, \
|
||||
"%s:%d: %s: %s\n", __FILE__, __LINE__, msg, mdb_strerror(rc)), abort()))
|
||||
|
||||
int main(int argc,char * argv[])
|
||||
{
|
||||
int i = 0, j = 0, rc;
|
||||
MDB_env *env;
|
||||
MDB_dbi dbi;
|
||||
MDB_val key, data;
|
||||
MDB_txn *txn;
|
||||
MDB_stat mst;
|
||||
MDB_cursor *cursor;
|
||||
int count;
|
||||
int *values;
|
||||
char sval[8];
|
||||
char kval[sizeof(int)];
|
||||
|
||||
memset(sval, 0, sizeof(sval));
|
||||
|
||||
count = 510;
|
||||
values = (int *)malloc(count*sizeof(int));
|
||||
|
||||
for(i = 0;i<count;i++) {
|
||||
values[i] = i*5;
|
||||
}
|
||||
|
||||
E(mdb_env_create(&env));
|
||||
E(mdb_env_set_mapsize(env, 10485760));
|
||||
E(mdb_env_set_maxdbs(env, 4));
|
||||
E(mdb_env_open(env, "./testdb", MDB_FIXEDMAP|MDB_NOSYNC, 0664));
|
||||
E(mdb_txn_begin(env, NULL, 0, &txn));
|
||||
E(mdb_open(txn, "id4", MDB_CREATE|MDB_DUPSORT|MDB_DUPFIXED, &dbi));
|
||||
|
||||
key.mv_size = sizeof(int);
|
||||
key.mv_data = kval;
|
||||
data.mv_size = sizeof(sval);
|
||||
data.mv_data = sval;
|
||||
|
||||
printf("Adding %d values\n", count);
|
||||
strcpy(kval, "001");
|
||||
for (i=0;i<count;i++) {
|
||||
sprintf(sval, "%07x", values[i]);
|
||||
if (RES(MDB_KEYEXIST, mdb_put(txn, dbi, &key, &data, MDB_NODUPDATA)))
|
||||
j++;
|
||||
}
|
||||
if (j) printf("%d duplicates skipped\n", j);
|
||||
E(mdb_txn_commit(txn));
|
||||
E(mdb_env_stat(env, &mst));
|
||||
|
||||
/* there should be one full page of dups now.
|
||||
*/
|
||||
E(mdb_txn_begin(env, NULL, 1, &txn));
|
||||
E(mdb_cursor_open(txn, dbi, &cursor));
|
||||
while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) {
|
||||
printf("key: %p %.*s, data: %p %.*s\n",
|
||||
key.mv_data, (int) key.mv_size, (char *) key.mv_data,
|
||||
data.mv_data, (int) data.mv_size, (char *) data.mv_data);
|
||||
}
|
||||
CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get");
|
||||
mdb_cursor_close(cursor);
|
||||
mdb_txn_abort(txn);
|
||||
|
||||
/* test all 3 branches of split code:
|
||||
* 1: new key in lower half
|
||||
* 2: new key at split point
|
||||
* 3: new key in upper half
|
||||
*/
|
||||
|
||||
key.mv_size = sizeof(int);
|
||||
key.mv_data = kval;
|
||||
data.mv_size = sizeof(sval);
|
||||
data.mv_data = sval;
|
||||
|
||||
sprintf(sval, "%07x", values[3]+1);
|
||||
E(mdb_txn_begin(env, NULL, 0, &txn));
|
||||
(void)RES(MDB_KEYEXIST, mdb_put(txn, dbi, &key, &data, MDB_NODUPDATA));
|
||||
mdb_txn_abort(txn);
|
||||
|
||||
sprintf(sval, "%07x", values[255]+1);
|
||||
E(mdb_txn_begin(env, NULL, 0, &txn));
|
||||
(void)RES(MDB_KEYEXIST, mdb_put(txn, dbi, &key, &data, MDB_NODUPDATA));
|
||||
mdb_txn_abort(txn);
|
||||
|
||||
sprintf(sval, "%07x", values[500]+1);
|
||||
E(mdb_txn_begin(env, NULL, 0, &txn));
|
||||
(void)RES(MDB_KEYEXIST, mdb_put(txn, dbi, &key, &data, MDB_NODUPDATA));
|
||||
E(mdb_txn_commit(txn));
|
||||
|
||||
/* Try MDB_NEXT_MULTIPLE */
|
||||
E(mdb_txn_begin(env, NULL, 0, &txn));
|
||||
E(mdb_cursor_open(txn, dbi, &cursor));
|
||||
while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT_MULTIPLE)) == 0) {
|
||||
printf("key: %.*s, data: %.*s\n",
|
||||
(int) key.mv_size, (char *) key.mv_data,
|
||||
(int) data.mv_size, (char *) data.mv_data);
|
||||
}
|
||||
CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get");
|
||||
mdb_cursor_close(cursor);
|
||||
mdb_txn_abort(txn);
|
||||
j=0;
|
||||
|
||||
for (i= count - 1; i > -1; i-= (rand()%3)) {
|
||||
j++;
|
||||
txn=NULL;
|
||||
E(mdb_txn_begin(env, NULL, 0, &txn));
|
||||
sprintf(sval, "%07x", values[i]);
|
||||
key.mv_size = sizeof(int);
|
||||
key.mv_data = kval;
|
||||
data.mv_size = sizeof(sval);
|
||||
data.mv_data = sval;
|
||||
if (RES(MDB_NOTFOUND, mdb_del(txn, dbi, &key, &data))) {
|
||||
j--;
|
||||
mdb_txn_abort(txn);
|
||||
} else {
|
||||
E(mdb_txn_commit(txn));
|
||||
}
|
||||
}
|
||||
free(values);
|
||||
printf("Deleted %d values\n", j);
|
||||
|
||||
E(mdb_env_stat(env, &mst));
|
||||
E(mdb_txn_begin(env, NULL, 1, &txn));
|
||||
E(mdb_cursor_open(txn, dbi, &cursor));
|
||||
printf("Cursor next\n");
|
||||
while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) {
|
||||
printf("key: %.*s, data: %.*s\n",
|
||||
(int) key.mv_size, (char *) key.mv_data,
|
||||
(int) data.mv_size, (char *) data.mv_data);
|
||||
}
|
||||
CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get");
|
||||
printf("Cursor prev\n");
|
||||
while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_PREV)) == 0) {
|
||||
printf("key: %.*s, data: %.*s\n",
|
||||
(int) key.mv_size, (char *) key.mv_data,
|
||||
(int) data.mv_size, (char *) data.mv_data);
|
||||
}
|
||||
CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get");
|
||||
mdb_cursor_close(cursor);
|
||||
mdb_close(env, dbi);
|
||||
|
||||
mdb_txn_abort(txn);
|
||||
mdb_env_close(env);
|
||||
|
||||
return 0;
|
||||
}
|
||||
135
submodules/lmdb/libraries/liblmdb/mtest5.c
Normal file
135
submodules/lmdb/libraries/liblmdb/mtest5.c
Normal file
@@ -0,0 +1,135 @@
|
||||
/* mtest5.c - memory-mapped database tester/toy */
|
||||
/*
|
||||
* Copyright 2011-2014 Howard Chu, Symas Corp.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted only as authorized by the OpenLDAP
|
||||
* Public License.
|
||||
*
|
||||
* A copy of this license is available in the file LICENSE in the
|
||||
* top-level directory of the distribution or, alternatively, at
|
||||
* <http://www.OpenLDAP.org/license.html>.
|
||||
*/
|
||||
|
||||
/* Tests for sorted duplicate DBs using cursor_put */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include "lmdb.h"
|
||||
|
||||
#define E(expr) CHECK((rc = (expr)) == MDB_SUCCESS, #expr)
|
||||
#define RES(err, expr) ((rc = expr) == (err) || (CHECK(!rc, #expr), 0))
|
||||
#define CHECK(test, msg) ((test) ? (void)0 : ((void)fprintf(stderr, \
|
||||
"%s:%d: %s: %s\n", __FILE__, __LINE__, msg, mdb_strerror(rc)), abort()))
|
||||
|
||||
int main(int argc,char * argv[])
|
||||
{
|
||||
int i = 0, j = 0, rc;
|
||||
MDB_env *env;
|
||||
MDB_dbi dbi;
|
||||
MDB_val key, data;
|
||||
MDB_txn *txn;
|
||||
MDB_stat mst;
|
||||
MDB_cursor *cursor;
|
||||
int count;
|
||||
int *values;
|
||||
char sval[32];
|
||||
char kval[sizeof(int)];
|
||||
|
||||
srand(time(NULL));
|
||||
|
||||
memset(sval, 0, sizeof(sval));
|
||||
|
||||
count = (rand()%384) + 64;
|
||||
values = (int *)malloc(count*sizeof(int));
|
||||
|
||||
for(i = 0;i<count;i++) {
|
||||
values[i] = rand()%1024;
|
||||
}
|
||||
|
||||
E(mdb_env_create(&env));
|
||||
E(mdb_env_set_mapsize(env, 10485760));
|
||||
E(mdb_env_set_maxdbs(env, 4));
|
||||
E(mdb_env_open(env, "./testdb", MDB_FIXEDMAP|MDB_NOSYNC, 0664));
|
||||
E(mdb_txn_begin(env, NULL, 0, &txn));
|
||||
E(mdb_open(txn, "id2", MDB_CREATE|MDB_DUPSORT, &dbi));
|
||||
E(mdb_cursor_open(txn, dbi, &cursor));
|
||||
|
||||
key.mv_size = sizeof(int);
|
||||
key.mv_data = kval;
|
||||
data.mv_size = sizeof(sval);
|
||||
data.mv_data = sval;
|
||||
|
||||
printf("Adding %d values\n", count);
|
||||
for (i=0;i<count;i++) {
|
||||
if (!(i & 0x0f))
|
||||
sprintf(kval, "%03x", values[i]);
|
||||
sprintf(sval, "%03x %d foo bar", values[i], values[i]);
|
||||
if (RES(MDB_KEYEXIST, mdb_cursor_put(cursor, &key, &data, MDB_NODUPDATA)))
|
||||
j++;
|
||||
}
|
||||
if (j) printf("%d duplicates skipped\n", j);
|
||||
mdb_cursor_close(cursor);
|
||||
E(mdb_txn_commit(txn));
|
||||
E(mdb_env_stat(env, &mst));
|
||||
|
||||
E(mdb_txn_begin(env, NULL, 1, &txn));
|
||||
E(mdb_cursor_open(txn, dbi, &cursor));
|
||||
while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) {
|
||||
printf("key: %p %.*s, data: %p %.*s\n",
|
||||
key.mv_data, (int) key.mv_size, (char *) key.mv_data,
|
||||
data.mv_data, (int) data.mv_size, (char *) data.mv_data);
|
||||
}
|
||||
CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get");
|
||||
mdb_cursor_close(cursor);
|
||||
mdb_txn_abort(txn);
|
||||
|
||||
j=0;
|
||||
|
||||
for (i= count - 1; i > -1; i-= (rand()%5)) {
|
||||
j++;
|
||||
txn=NULL;
|
||||
E(mdb_txn_begin(env, NULL, 0, &txn));
|
||||
sprintf(kval, "%03x", values[i & ~0x0f]);
|
||||
sprintf(sval, "%03x %d foo bar", values[i], values[i]);
|
||||
key.mv_size = sizeof(int);
|
||||
key.mv_data = kval;
|
||||
data.mv_size = sizeof(sval);
|
||||
data.mv_data = sval;
|
||||
if (RES(MDB_NOTFOUND, mdb_del(txn, dbi, &key, &data))) {
|
||||
j--;
|
||||
mdb_txn_abort(txn);
|
||||
} else {
|
||||
E(mdb_txn_commit(txn));
|
||||
}
|
||||
}
|
||||
free(values);
|
||||
printf("Deleted %d values\n", j);
|
||||
|
||||
E(mdb_env_stat(env, &mst));
|
||||
E(mdb_txn_begin(env, NULL, 1, &txn));
|
||||
E(mdb_cursor_open(txn, dbi, &cursor));
|
||||
printf("Cursor next\n");
|
||||
while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) {
|
||||
printf("key: %.*s, data: %.*s\n",
|
||||
(int) key.mv_size, (char *) key.mv_data,
|
||||
(int) data.mv_size, (char *) data.mv_data);
|
||||
}
|
||||
CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get");
|
||||
printf("Cursor prev\n");
|
||||
while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_PREV)) == 0) {
|
||||
printf("key: %.*s, data: %.*s\n",
|
||||
(int) key.mv_size, (char *) key.mv_data,
|
||||
(int) data.mv_size, (char *) data.mv_data);
|
||||
}
|
||||
CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get");
|
||||
mdb_cursor_close(cursor);
|
||||
mdb_close(env, dbi);
|
||||
|
||||
mdb_txn_abort(txn);
|
||||
mdb_env_close(env);
|
||||
|
||||
return 0;
|
||||
}
|
||||
137
submodules/lmdb/libraries/liblmdb/mtest6.c
Normal file
137
submodules/lmdb/libraries/liblmdb/mtest6.c
Normal file
@@ -0,0 +1,137 @@
|
||||
/* mtest6.c - memory-mapped database tester/toy */
|
||||
/*
|
||||
* Copyright 2011-2014 Howard Chu, Symas Corp.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted only as authorized by the OpenLDAP
|
||||
* Public License.
|
||||
*
|
||||
* A copy of this license is available in the file LICENSE in the
|
||||
* top-level directory of the distribution or, alternatively, at
|
||||
* <http://www.OpenLDAP.org/license.html>.
|
||||
*/
|
||||
|
||||
/* Tests for DB splits and merges */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include "lmdb.h"
|
||||
|
||||
#define E(expr) CHECK((rc = (expr)) == MDB_SUCCESS, #expr)
|
||||
#define RES(err, expr) ((rc = expr) == (err) || (CHECK(!rc, #expr), 0))
|
||||
#define CHECK(test, msg) ((test) ? (void)0 : ((void)fprintf(stderr, \
|
||||
"%s:%d: %s: %s\n", __FILE__, __LINE__, msg, mdb_strerror(rc)), abort()))
|
||||
|
||||
char dkbuf[1024];
|
||||
|
||||
int main(int argc,char * argv[])
|
||||
{
|
||||
int i = 0, j = 0, rc;
|
||||
MDB_env *env;
|
||||
MDB_dbi dbi;
|
||||
MDB_val key, data;
|
||||
MDB_txn *txn;
|
||||
MDB_stat mst;
|
||||
MDB_cursor *cursor;
|
||||
int count;
|
||||
int *values;
|
||||
long kval;
|
||||
char *sval;
|
||||
|
||||
srand(time(NULL));
|
||||
|
||||
E(mdb_env_create(&env));
|
||||
E(mdb_env_set_mapsize(env, 10485760));
|
||||
E(mdb_env_set_maxdbs(env, 4));
|
||||
E(mdb_env_open(env, "./testdb", MDB_FIXEDMAP|MDB_NOSYNC, 0664));
|
||||
E(mdb_txn_begin(env, NULL, 0, &txn));
|
||||
E(mdb_open(txn, "id6", MDB_CREATE|MDB_INTEGERKEY, &dbi));
|
||||
E(mdb_cursor_open(txn, dbi, &cursor));
|
||||
E(mdb_stat(txn, dbi, &mst));
|
||||
|
||||
sval = calloc(1, mst.ms_psize / 4);
|
||||
key.mv_size = sizeof(long);
|
||||
key.mv_data = &kval;
|
||||
data.mv_size = mst.ms_psize / 4 - 30;
|
||||
data.mv_data = sval;
|
||||
|
||||
printf("Adding 12 values, should yield 3 splits\n");
|
||||
for (i=0;i<12;i++) {
|
||||
kval = i*5;
|
||||
sprintf(sval, "%08x", kval);
|
||||
(void)RES(MDB_KEYEXIST, mdb_cursor_put(cursor, &key, &data, MDB_NOOVERWRITE));
|
||||
}
|
||||
printf("Adding 12 more values, should yield 3 splits\n");
|
||||
for (i=0;i<12;i++) {
|
||||
kval = i*5+4;
|
||||
sprintf(sval, "%08x", kval);
|
||||
(void)RES(MDB_KEYEXIST, mdb_cursor_put(cursor, &key, &data, MDB_NOOVERWRITE));
|
||||
}
|
||||
printf("Adding 12 more values, should yield 3 splits\n");
|
||||
for (i=0;i<12;i++) {
|
||||
kval = i*5+1;
|
||||
sprintf(sval, "%08x", kval);
|
||||
(void)RES(MDB_KEYEXIST, mdb_cursor_put(cursor, &key, &data, MDB_NOOVERWRITE));
|
||||
}
|
||||
E(mdb_cursor_get(cursor, &key, &data, MDB_FIRST));
|
||||
|
||||
do {
|
||||
printf("key: %p %s, data: %p %.*s\n",
|
||||
key.mv_data, mdb_dkey(&key, dkbuf),
|
||||
data.mv_data, (int) data.mv_size, (char *) data.mv_data);
|
||||
} while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0);
|
||||
CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get");
|
||||
mdb_cursor_close(cursor);
|
||||
mdb_txn_commit(txn);
|
||||
|
||||
#if 0
|
||||
j=0;
|
||||
|
||||
for (i= count - 1; i > -1; i-= (rand()%5)) {
|
||||
j++;
|
||||
txn=NULL;
|
||||
E(mdb_txn_begin(env, NULL, 0, &txn));
|
||||
sprintf(kval, "%03x", values[i & ~0x0f]);
|
||||
sprintf(sval, "%03x %d foo bar", values[i], values[i]);
|
||||
key.mv_size = sizeof(int);
|
||||
key.mv_data = kval;
|
||||
data.mv_size = sizeof(sval);
|
||||
data.mv_data = sval;
|
||||
if (RES(MDB_NOTFOUND, mdb_del(txn, dbi, &key, &data))) {
|
||||
j--;
|
||||
mdb_txn_abort(txn);
|
||||
} else {
|
||||
E(mdb_txn_commit(txn));
|
||||
}
|
||||
}
|
||||
free(values);
|
||||
printf("Deleted %d values\n", j);
|
||||
|
||||
E(mdb_env_stat(env, &mst));
|
||||
E(mdb_txn_begin(env, NULL, 1, &txn));
|
||||
E(mdb_cursor_open(txn, dbi, &cursor));
|
||||
printf("Cursor next\n");
|
||||
while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) {
|
||||
printf("key: %.*s, data: %.*s\n",
|
||||
(int) key.mv_size, (char *) key.mv_data,
|
||||
(int) data.mv_size, (char *) data.mv_data);
|
||||
}
|
||||
CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get");
|
||||
printf("Cursor prev\n");
|
||||
while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_PREV)) == 0) {
|
||||
printf("key: %.*s, data: %.*s\n",
|
||||
(int) key.mv_size, (char *) key.mv_data,
|
||||
(int) data.mv_size, (char *) data.mv_data);
|
||||
}
|
||||
CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get");
|
||||
mdb_cursor_close(cursor);
|
||||
mdb_close(env, dbi);
|
||||
|
||||
mdb_txn_abort(txn);
|
||||
#endif
|
||||
mdb_env_close(env);
|
||||
|
||||
return 0;
|
||||
}
|
||||
73
submodules/lmdb/libraries/liblmdb/sample-bdb.txt
Normal file
73
submodules/lmdb/libraries/liblmdb/sample-bdb.txt
Normal file
@@ -0,0 +1,73 @@
|
||||
/* sample-bdb.txt - BerkeleyDB toy/sample
|
||||
*
|
||||
* Do a line-by-line comparison of this and sample-mdb.txt
|
||||
*/
|
||||
/*
|
||||
* Copyright 2012 Howard Chu, Symas Corp.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted only as authorized by the OpenLDAP
|
||||
* Public License.
|
||||
*
|
||||
* A copy of this license is available in the file LICENSE in the
|
||||
* top-level directory of the distribution or, alternatively, at
|
||||
* <http://www.OpenLDAP.org/license.html>.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <db.h>
|
||||
|
||||
int main(int argc,char * argv[])
|
||||
{
|
||||
int rc;
|
||||
DB_ENV *env;
|
||||
DB *dbi;
|
||||
DBT key, data;
|
||||
DB_TXN *txn;
|
||||
DBC *cursor;
|
||||
char sval[32], kval[32];
|
||||
|
||||
/* Note: Most error checking omitted for simplicity */
|
||||
|
||||
#define FLAGS (DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_INIT_MPOOL|DB_CREATE|DB_THREAD)
|
||||
rc = db_env_create(&env, 0);
|
||||
rc = env->open(env, "./testdb", FLAGS, 0664);
|
||||
rc = db_create(&dbi, env, 0);
|
||||
rc = env->txn_begin(env, NULL, &txn, 0);
|
||||
rc = dbi->open(dbi, txn, "test.bdb", NULL, DB_BTREE, DB_CREATE, 0664);
|
||||
|
||||
memset(&key, 0, sizeof(DBT));
|
||||
memset(&data, 0, sizeof(DBT));
|
||||
key.size = sizeof(int);
|
||||
key.data = sval;
|
||||
data.size = sizeof(sval);
|
||||
data.data = sval;
|
||||
|
||||
sprintf(sval, "%03x %d foo bar", 32, 3141592);
|
||||
rc = dbi->put(dbi, txn, &key, &data, 0);
|
||||
rc = txn->commit(txn, 0);
|
||||
if (rc) {
|
||||
fprintf(stderr, "txn->commit: (%d) %s\n", rc, db_strerror(rc));
|
||||
goto leave;
|
||||
}
|
||||
rc = env->txn_begin(env, NULL, &txn, 0);
|
||||
rc = dbi->cursor(dbi, txn, &cursor, 0);
|
||||
key.flags = DB_DBT_USERMEM;
|
||||
key.data = kval;
|
||||
key.ulen = sizeof(kval);
|
||||
data.flags = DB_DBT_USERMEM;
|
||||
data.data = sval;
|
||||
data.ulen = sizeof(sval);
|
||||
while ((rc = cursor->c_get(cursor, &key, &data, DB_NEXT)) == 0) {
|
||||
printf("key: %p %.*s, data: %p %.*s\n",
|
||||
key.data, (int) key.size, (char *) key.data,
|
||||
data.data, (int) data.size, (char *) data.data);
|
||||
}
|
||||
rc = cursor->c_close(cursor);
|
||||
rc = txn->abort(txn);
|
||||
leave:
|
||||
rc = dbi->close(dbi, 0);
|
||||
rc = env->close(env, 0);
|
||||
return rc;
|
||||
}
|
||||
62
submodules/lmdb/libraries/liblmdb/sample-mdb.txt
Normal file
62
submodules/lmdb/libraries/liblmdb/sample-mdb.txt
Normal file
@@ -0,0 +1,62 @@
|
||||
/* sample-mdb.txt - MDB toy/sample
|
||||
*
|
||||
* Do a line-by-line comparison of this and sample-bdb.txt
|
||||
*/
|
||||
/*
|
||||
* Copyright 2012 Howard Chu, Symas Corp.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted only as authorized by the OpenLDAP
|
||||
* Public License.
|
||||
*
|
||||
* A copy of this license is available in the file LICENSE in the
|
||||
* top-level directory of the distribution or, alternatively, at
|
||||
* <http://www.OpenLDAP.org/license.html>.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include "lmdb.h"
|
||||
|
||||
int main(int argc,char * argv[])
|
||||
{
|
||||
int rc;
|
||||
MDB_env *env;
|
||||
MDB_dbi dbi;
|
||||
MDB_val key, data;
|
||||
MDB_txn *txn;
|
||||
MDB_cursor *cursor;
|
||||
char sval[32];
|
||||
|
||||
/* Note: Most error checking omitted for simplicity */
|
||||
|
||||
rc = mdb_env_create(&env);
|
||||
rc = mdb_env_open(env, "./testdb", 0, 0664);
|
||||
rc = mdb_txn_begin(env, NULL, 0, &txn);
|
||||
rc = mdb_open(txn, NULL, 0, &dbi);
|
||||
|
||||
key.mv_size = sizeof(int);
|
||||
key.mv_data = sval;
|
||||
data.mv_size = sizeof(sval);
|
||||
data.mv_data = sval;
|
||||
|
||||
sprintf(sval, "%03x %d foo bar", 32, 3141592);
|
||||
rc = mdb_put(txn, dbi, &key, &data, 0);
|
||||
rc = mdb_txn_commit(txn);
|
||||
if (rc) {
|
||||
fprintf(stderr, "mdb_txn_commit: (%d) %s\n", rc, mdb_strerror(rc));
|
||||
goto leave;
|
||||
}
|
||||
rc = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn);
|
||||
rc = mdb_cursor_open(txn, dbi, &cursor);
|
||||
while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) {
|
||||
printf("key: %p %.*s, data: %p %.*s\n",
|
||||
key.mv_data, (int) key.mv_size, (char *) key.mv_data,
|
||||
data.mv_data, (int) data.mv_size, (char *) data.mv_data);
|
||||
}
|
||||
mdb_cursor_close(cursor);
|
||||
mdb_txn_abort(txn);
|
||||
leave:
|
||||
mdb_close(env, dbi);
|
||||
mdb_env_close(env);
|
||||
return 0;
|
||||
}
|
||||
22
submodules/lmdb/libraries/liblmdb/tooltag
Normal file
22
submodules/lmdb/libraries/liblmdb/tooltag
Normal file
@@ -0,0 +1,22 @@
|
||||
<tagfile>
|
||||
<compound kind="page">
|
||||
<name>mdb_copy_1</name>
|
||||
<title>mdb_copy - environment copy tool</title>
|
||||
<filename>mdb_copy.1</filename>
|
||||
</compound>
|
||||
<compound kind="page">
|
||||
<name>mdb_dump_1</name>
|
||||
<title>mdb_dump - environment export tool</title>
|
||||
<filename>mdb_dump.1</filename>
|
||||
</compound>
|
||||
<compound kind="page">
|
||||
<name>mdb_load_1</name>
|
||||
<title>mdb_load - environment import tool</title>
|
||||
<filename>mdb_load.1</filename>
|
||||
</compound>
|
||||
<compound kind="page">
|
||||
<name>mdb_stat_1</name>
|
||||
<title>mdb_stat - environment status tool</title>
|
||||
<filename>mdb_stat.1</filename>
|
||||
</compound>
|
||||
</tagfile>
|
||||
Reference in New Issue
Block a user