├── docs ├── contributing.md ├── indexing │ ├── enumerate.md │ └── indexed_list.md ├── template │ ├── CNAME │ ├── images │ │ └── logo │ │ │ ├── colors.txt │ │ │ ├── icon_blue_32x32.png │ │ │ ├── logo_illustrator.ai │ │ │ ├── icon_blue_512x512.png │ │ │ ├── icon_white.svg │ │ │ └── icon_blue.svg │ ├── screenshot.png │ ├── fonts │ │ ├── FontAwesome.otf │ │ ├── fontawesome-webfont.eot │ │ ├── fontawesome-webfont.ttf │ │ └── fontawesome-webfont.woff │ ├── couscous.yml │ ├── LICENSE │ ├── README.md │ └── css │ │ ├── highlight.tomorrow-night.css │ │ └── main.css ├── encryption.md ├── upgrade05.md ├── persisting.md ├── upgrade04.md ├── transactions.md ├── longterm.md ├── README.md ├── roadmap.md └── reading.md ├── BuildArtifacts └── wpa81 │ └── MarcelloDB.W81.dll ├── UniversalTest ├── UniversalTest.Windows │ ├── Assets │ │ ├── Logo.scale-100.png │ │ ├── SmallLogo.scale-100.png │ │ ├── StoreLogo.scale-100.png │ │ └── SplashScreen.scale-100.png │ ├── MainPage.xaml │ ├── MainPage.xaml.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ └── Package.appxmanifest ├── UniversalTest.WindowsPhone │ ├── Assets │ │ ├── Logo.scale-240.png │ │ ├── SmallLogo.scale-240.png │ │ ├── StoreLogo.scale-240.png │ │ ├── WideLogo.scale-240.png │ │ ├── SplashScreen.scale-240.png │ │ └── Square71x71Logo.scale-240.png │ ├── MainPage.xaml │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── MainPage.xaml.cs │ └── Package.appxmanifest └── UniversalTest.Shared │ ├── App.xaml │ ├── Article.cs │ ├── UniversalTest.Shared.projitems │ └── UniversalTest.Shared.shproj ├── MarcelloDB ├── Serialization │ ├── NodeSerializer.cs │ ├── IObjectSerializer.cs │ ├── ValueSerializers │ │ ├── StringSerializer.cs │ │ ├── ByteSerializer.cs │ │ ├── BooleanSerializers.cs │ │ ├── DateTimeSerializer.cs │ │ ├── ValueWithAddressIndexKeySerializer.cs │ │ ├── IntSerializer.cs │ │ └── FloatSerializer.cs │ ├── JsonSerializer.cs │ ├── BsonSerializer.cs │ ├── BtreeNodeSerializer.cs │ ├── IndexMetaRecordSerializer.cs │ ├── CollectionFileRootSerializer.cs │ ├── SerializerResolver.cs │ ├── BufferReader.cs │ ├── BufferWriter.cs │ └── TransactionJournalSerializer.cs ├── packages.config ├── Transactions │ ├── ITransactor.cs │ ├── JournalEntry.cs │ └── Transaction.cs ├── Storage │ ├── IStorageStreamProvider.cs │ ├── IStorageStream.cs │ ├── StreamActors │ │ ├── Writer.cs │ │ ├── Reader.cs │ │ ├── JournalledWriter.cs │ │ ├── StreamActor.cs │ │ └── JournalledReader.cs │ └── StorageEngine.cs ├── Platform │ └── IPlatform.cs ├── AllocationStrategies │ ├── IAllocationStrategy.cs │ ├── DoubleSizeAllocationStrategy.cs │ ├── ExactSizeAllocationStrategy.cs │ ├── AllocationStrategyResolver.cs │ └── PredictiveBTreeNodeAllocationStrategy.cs ├── Exceptions │ ├── IDMissingException.cs │ └── ObjectNotFoundException.cs ├── Records │ ├── NamedRecordsIndex.cs │ ├── CollectionMetaDataT.cs │ ├── EmptyRecordIndexKey.cs │ ├── ValueWithAddressIndexKey.cs │ ├── Record.cs │ ├── RecordHeader.cs │ ├── CollectionFileRoot.cs │ └── IndexMetaRecord.cs ├── Index │ ├── BTree │ │ └── IBtreeDataProvider.cs │ ├── AddressList.cs │ ├── EntryList.cs │ ├── EmptyRecordIndex.cs │ ├── Entry.cs │ ├── Node.cs │ ├── CompoundValue.cs │ └── ObjectComparer.cs ├── Collections │ ├── Scopes │ │ ├── Descending.cs │ │ ├── All.cs │ │ ├── GreaterThan.cs │ │ ├── SmallerThan.cs │ │ └── BaseScope.cs │ ├── ObjectByIDEnumerator.cs │ ├── IndexedValue.cs │ ├── IndexedValueTypes │ │ ├── UniqueIndexedValue.cs │ │ ├── IndexedIDValue.cs │ │ └── IndexedList.cs │ ├── KeysEnumeratorT.cs │ └── IndexEntryEnumerator.cs ├── Properties │ └── AssemblyInfo.cs └── Helpers │ └── DataHelper.cs ├── MarcelloDB.Test ├── packages.config ├── AllocationStrategies │ ├── ExactSizeAllocationStrategy.cs │ ├── DoubleSizeAllocationStrategyTest.cs │ ├── AllocationStrategyResolverTest.cs │ └── PredictiveBTreeNodeAllocationStrategyTest.cs ├── TestStream │ ├── TestPlatform.cs │ └── InMemoryStream.cs ├── Classes │ ├── Location.cs │ ├── ArticleIndexDefinition.cs │ └── Article.cs ├── Records │ ├── RecordHeaderTest.cs │ ├── EmptyRecordIndexKeyTest.cs │ └── ValueWithAddressIndexKeyTest.cs ├── Regression │ ├── Issue32_OutOfMemoryOnSecondPersist.cs │ ├── Issue31_NullReferenceWhenPersistingWithNonID.cs │ ├── Issue74_ItemWithSameKeyAllreadyAdded.cs │ ├── Issue49_DateTime_Indexed_Value.cs │ ├── Issue69_OutOfMemoryOnEmptyCollection.cs │ └── Issue67_RootNotSavedAfterRecycle.cs ├── Index │ ├── Mocks │ │ ├── MockBTree.cs │ │ └── MockBTreeDataProvider.cs │ ├── NodeTest.cs │ ├── ObjectComparerTest.cs │ ├── IndexTest.cs │ └── IndexDefinitionTest.cs └── Serialization │ ├── CollectionFileRootSerializerTest.cs │ ├── BsonSerializerTest.cs │ ├── TransactionJournalSerializerTest.cs │ ├── IndexMetaRecordSerializerTest.cs │ └── EmptyRecordIndexNodeSerializerTest.cs ├── MarcelloDB.BenchmarkTool ├── DataClasses │ ├── Base.cs │ ├── Address.cs │ └── Person.cs ├── Extensions │ └── ListExtensions.cs ├── Benchmarks │ ├── RandomIDsBulkInsert.cs │ ├── SequentialIDsBulkInsert.cs │ ├── SequentialDestroy.cs │ ├── RandomRead.cs │ ├── RandomDestroy.cs │ ├── EnumerateAll.cs │ └── Base.cs └── Properties │ └── AssemblyInfo.cs ├── .travis.yml ├── MarcelloDB.netfx ├── Platform.cs ├── Properties │ └── AssemblyInfo.cs └── MarcelloDB.netfx.csproj ├── MarcelloDB.uwp ├── Platform.cs └── Properties │ ├── AssemblyInfo.cs │ └── MarcelloDB.uwp.rd.xml ├── MarcelloDB.W81 ├── Platform.cs ├── Properties │ └── AssemblyInfo.cs └── MarcelloDB.W81.csproj ├── Package └── MarcelloDB.nuspec ├── LICENSE.txt ├── README.md └── couscous.yml /docs/contributing.md: -------------------------------------------------------------------------------- 1 | contributing.md -------------------------------------------------------------------------------- /docs/indexing/enumerate.md: -------------------------------------------------------------------------------- 1 | enumerate.md -------------------------------------------------------------------------------- /docs/template/CNAME: -------------------------------------------------------------------------------- 1 | www.marcellodb.org -------------------------------------------------------------------------------- /docs/template/images/logo/colors.txt: -------------------------------------------------------------------------------- 1 | blue #2062c5 -------------------------------------------------------------------------------- /docs/template/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markmeeus/MarcelloDB/HEAD/docs/template/screenshot.png -------------------------------------------------------------------------------- /docs/template/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markmeeus/MarcelloDB/HEAD/docs/template/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /BuildArtifacts/wpa81/MarcelloDB.W81.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markmeeus/MarcelloDB/HEAD/BuildArtifacts/wpa81/MarcelloDB.W81.dll -------------------------------------------------------------------------------- /docs/template/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markmeeus/MarcelloDB/HEAD/docs/template/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /docs/template/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markmeeus/MarcelloDB/HEAD/docs/template/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /docs/template/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markmeeus/MarcelloDB/HEAD/docs/template/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /docs/template/images/logo/icon_blue_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markmeeus/MarcelloDB/HEAD/docs/template/images/logo/icon_blue_32x32.png -------------------------------------------------------------------------------- /docs/template/images/logo/logo_illustrator.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markmeeus/MarcelloDB/HEAD/docs/template/images/logo/logo_illustrator.ai -------------------------------------------------------------------------------- /docs/template/images/logo/icon_blue_512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markmeeus/MarcelloDB/HEAD/docs/template/images/logo/icon_blue_512x512.png -------------------------------------------------------------------------------- /UniversalTest/UniversalTest.Windows/Assets/Logo.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markmeeus/MarcelloDB/HEAD/UniversalTest/UniversalTest.Windows/Assets/Logo.scale-100.png -------------------------------------------------------------------------------- /UniversalTest/UniversalTest.Windows/Assets/SmallLogo.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markmeeus/MarcelloDB/HEAD/UniversalTest/UniversalTest.Windows/Assets/SmallLogo.scale-100.png -------------------------------------------------------------------------------- /UniversalTest/UniversalTest.Windows/Assets/StoreLogo.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markmeeus/MarcelloDB/HEAD/UniversalTest/UniversalTest.Windows/Assets/StoreLogo.scale-100.png -------------------------------------------------------------------------------- /UniversalTest/UniversalTest.WindowsPhone/Assets/Logo.scale-240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markmeeus/MarcelloDB/HEAD/UniversalTest/UniversalTest.WindowsPhone/Assets/Logo.scale-240.png -------------------------------------------------------------------------------- /UniversalTest/UniversalTest.Windows/Assets/SplashScreen.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markmeeus/MarcelloDB/HEAD/UniversalTest/UniversalTest.Windows/Assets/SplashScreen.scale-100.png -------------------------------------------------------------------------------- /UniversalTest/UniversalTest.WindowsPhone/Assets/SmallLogo.scale-240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markmeeus/MarcelloDB/HEAD/UniversalTest/UniversalTest.WindowsPhone/Assets/SmallLogo.scale-240.png -------------------------------------------------------------------------------- /UniversalTest/UniversalTest.WindowsPhone/Assets/StoreLogo.scale-240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markmeeus/MarcelloDB/HEAD/UniversalTest/UniversalTest.WindowsPhone/Assets/StoreLogo.scale-240.png -------------------------------------------------------------------------------- /UniversalTest/UniversalTest.WindowsPhone/Assets/WideLogo.scale-240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markmeeus/MarcelloDB/HEAD/UniversalTest/UniversalTest.WindowsPhone/Assets/WideLogo.scale-240.png -------------------------------------------------------------------------------- /UniversalTest/UniversalTest.WindowsPhone/Assets/SplashScreen.scale-240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markmeeus/MarcelloDB/HEAD/UniversalTest/UniversalTest.WindowsPhone/Assets/SplashScreen.scale-240.png -------------------------------------------------------------------------------- /UniversalTest/UniversalTest.WindowsPhone/Assets/Square71x71Logo.scale-240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markmeeus/MarcelloDB/HEAD/UniversalTest/UniversalTest.WindowsPhone/Assets/Square71x71Logo.scale-240.png -------------------------------------------------------------------------------- /MarcelloDB/Serialization/NodeSerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MarcelloDB 4 | { 5 | public class NodeSerializer 6 | { 7 | public NodeSerializer() 8 | { 9 | } 10 | } 11 | } 12 | 13 | -------------------------------------------------------------------------------- /MarcelloDB/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /MarcelloDB.Test/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /MarcelloDB/Transactions/ITransactor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MarcelloDB.Transactions 4 | { 5 | internal interface ITransactor 6 | { 7 | void SaveState(); 8 | void RollbackState(); 9 | } 10 | } 11 | 12 | -------------------------------------------------------------------------------- /docs/encryption.md: -------------------------------------------------------------------------------- 1 | ##MarcelloDB.Encryption 2 | MarcelloDB's data files are unencrypted by default. 3 | An encryption layer is being developed and is included in the Pro/Support package. 4 | 5 | Please contact mark.meeus@gmail.com if interested. 6 | -------------------------------------------------------------------------------- /MarcelloDB.BenchmarkTool/DataClasses/Base.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MarcelloDB.BenchmarkTool.DataClasses 4 | { 5 | public class Base 6 | { 7 | public Base() 8 | { 9 | } 10 | } 11 | } 12 | 13 | -------------------------------------------------------------------------------- /MarcelloDB/Storage/IStorageStreamProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MarcelloDB.Storage 4 | { 5 | public interface IStorageStreamProvider: IDisposable 6 | { 7 | IStorageStream GetStream(string streamName); 8 | } 9 | } 10 | 11 | -------------------------------------------------------------------------------- /MarcelloDB/Platform/IPlatform.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using MarcelloDB.Storage; 3 | 4 | namespace MarcelloDB.Platform 5 | { 6 | public interface IPlatform 7 | { 8 | IStorageStreamProvider CreateStorageStreamProvider(string rootPath); 9 | } 10 | } 11 | 12 | -------------------------------------------------------------------------------- /MarcelloDB/Serialization/IObjectSerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MarcelloDB.Serialization 4 | { 5 | internal interface IObjectSerializer 6 | { 7 | byte[] Serialize(T obj); 8 | 9 | T Deserialize(byte[] bytes); 10 | } 11 | } 12 | 13 | -------------------------------------------------------------------------------- /MarcelloDB/Storage/IStorageStream.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MarcelloDB.Storage 4 | { 5 | public interface IStorageStream 6 | { 7 | byte[] Read(long address, int length); 8 | 9 | void Write(long address, byte[] bytes); 10 | } 11 | } 12 | 13 | -------------------------------------------------------------------------------- /MarcelloDB/AllocationStrategies/IAllocationStrategy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using MarcelloDB.Records; 3 | 4 | namespace MarcelloDB.AllocationStrategies 5 | { 6 | internal interface IAllocationStrategy 7 | { 8 | int CalculateSize(int dataSize); 9 | } 10 | } 11 | 12 | -------------------------------------------------------------------------------- /MarcelloDB/Exceptions/IDMissingException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MarcelloDB.Exceptions 4 | { 5 | public class IDMissingException : Exception 6 | { 7 | public IDMissingException(string Message) : base(Message) 8 | { 9 | } 10 | } 11 | } 12 | 13 | -------------------------------------------------------------------------------- /UniversalTest/UniversalTest.Shared/App.xaml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /MarcelloDB/Exceptions/ObjectNotFoundException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MarcelloDB.Exceptions 4 | { 5 | public class ObjectNotFoundException : Exception 6 | { 7 | public ObjectNotFoundException(string message) : base(message) 8 | { 9 | } 10 | } 11 | } 12 | 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: csharp 2 | solution: MarcelloDB.sln 3 | script: 4 | - nuget install NUnit.Runners -Version 2.6.4 -OutputDirectory testrunner 5 | - xbuild /p:Configuration=Release MarcelloDB.sln 6 | - mono ./testrunner/NUnit.Runners.2.6.4/tools/nunit-console.exe ./MarcelloDB.Test/bin/Release/MarcelloDB.Test.dll 7 | 8 | -------------------------------------------------------------------------------- /MarcelloDB/AllocationStrategies/DoubleSizeAllocationStrategy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using MarcelloDB.Records; 3 | 4 | namespace MarcelloDB.AllocationStrategies 5 | { 6 | internal class DoubleSizeAllocationStrategy : IAllocationStrategy 7 | { 8 | public int CalculateSize(int dataSize) 9 | { 10 | return dataSize * 2; 11 | } 12 | } 13 | } 14 | 15 | -------------------------------------------------------------------------------- /MarcelloDB/AllocationStrategies/ExactSizeAllocationStrategy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MarcelloDB.AllocationStrategies 4 | { 5 | internal class ExactSizeAllocationStrategy : IAllocationStrategy 6 | { 7 | #region IAllocationStrategy implementation 8 | 9 | public int CalculateSize(int dataSize) 10 | { 11 | return dataSize; 12 | } 13 | 14 | #endregion 15 | } 16 | } 17 | 18 | -------------------------------------------------------------------------------- /MarcelloDB.netfx/Platform.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using MarcelloDB.Platform; 3 | using System.Collections.Generic; 4 | using MarcelloDB.Storage; 5 | using MarcelloDB.netfx.Storage; 6 | 7 | namespace MarcelloDB.netfx 8 | { 9 | public class Platform : IPlatform 10 | { 11 | public IStorageStreamProvider CreateStorageStreamProvider(string rootPath) 12 | { 13 | return new FileStorageStreamProvider(rootPath); 14 | } 15 | } 16 | } 17 | 18 | -------------------------------------------------------------------------------- /MarcelloDB/Records/NamedRecordsIndex.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using MarcelloDB.Serialization; 4 | 5 | namespace MarcelloDB.Records 6 | { 7 | internal class NamedRecordsIndex 8 | { 9 | public Dictionary NamedRecordIndexes { get; set;} 10 | 11 | internal NamedRecordsIndex() 12 | { 13 | this.NamedRecordIndexes = new Dictionary(); 14 | } 15 | } 16 | } 17 | 18 | -------------------------------------------------------------------------------- /MarcelloDB/Storage/StreamActors/Writer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MarcelloDB.Storage.StreamActors 4 | { 5 | internal class Writer : StreamActor 6 | { 7 | internal Writer(Session session, string streamName) 8 | : base(session, streamName) 9 | { 10 | } 11 | 12 | internal virtual void Write(long address, byte[] bytes) 13 | { 14 | GetStream().Write (address, bytes); 15 | } 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /MarcelloDB/Storage/StreamActors/Reader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MarcelloDB.Storage.StreamActors 4 | { 5 | internal class Reader : StreamActor 6 | { 7 | internal Reader(Session session, string collectionName) 8 | :base(session, collectionName) 9 | { 10 | } 11 | 12 | internal virtual byte[] Read(long address, int length) 13 | { 14 | return this.GetStream().Read(address, length); 15 | } 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /UniversalTest/UniversalTest.Shared/Article.cs: -------------------------------------------------------------------------------- 1 | using MarcelloDB.Collections; 2 | using MarcelloDB.Index; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | 7 | namespace UniversalTest 8 | { 9 | public class ArticleIndex : IndexDefinition
10 | { 11 | public IndexedValue Name { get; set; } 12 | } 13 | 14 | public class Article 15 | { 16 | public int ID { get; set; } 17 | public string Name { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /MarcelloDB/Storage/StreamActors/JournalledWriter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MarcelloDB.Storage.StreamActors 4 | { 5 | internal class JournalledWriter : Writer 6 | { 7 | internal JournalledWriter(Session session, string streamName) 8 | : base(session, streamName) 9 | { 10 | } 11 | 12 | internal override void Write (long address, byte[] bytes) 13 | { 14 | Session.Journal.Write(this.StreamName, address, bytes); 15 | } 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /MarcelloDB/Index/BTree/IBtreeDataProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using MarcelloDB.AllocationStrategies; 4 | 5 | namespace MarcelloDB.Index.BTree 6 | { 7 | internal interface IBTreeDataProvider 8 | { 9 | Node GetRootNode(int degree); 10 | 11 | void SetRootNode(Node node); 12 | 13 | Node GetNode(Int64 address); 14 | 15 | Node CreateNode(int degree, bool allowRecordReuse = true); 16 | 17 | void Flush(); 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /MarcelloDB/Serialization/ValueSerializers/StringSerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MarcelloDB.Serialization.ValueSerializers 4 | { 5 | class StringSerializer : ValueSerializer 6 | { 7 | internal override String ReadValue(BinaryFormatter formatter) 8 | { 9 | return formatter.ReadString(); 10 | } 11 | internal override void WriteValue(BinaryFormatter formatter, String value) 12 | { 13 | formatter.WriteString(value); 14 | } 15 | } 16 | } 17 | 18 | -------------------------------------------------------------------------------- /MarcelloDB.Test/AllocationStrategies/ExactSizeAllocationStrategy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using MarcelloDB.AllocationStrategies; 4 | 5 | namespace MarcelloDB.Test.AllocationStrategies 6 | { 7 | [TestFixture] 8 | public class ExactSizeAllocationStrategyTest 9 | { 10 | [Test] 11 | public void Uses_Exact_Size() 12 | { 13 | var allocationStrategy = new ExactSizeAllocationStrategy(); 14 | Assert.AreEqual(100, allocationStrategy.CalculateSize(100)); 15 | } 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /UniversalTest/UniversalTest.Windows/MainPage.xaml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /MarcelloDB.Test/AllocationStrategies/DoubleSizeAllocationStrategyTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using MarcelloDB.AllocationStrategies; 4 | 5 | namespace MarcelloDB.Test.AllocationStrategies 6 | { 7 | [TestFixture] 8 | public class DoubleSizeAllocationStrategyTest 9 | { 10 | [Test] 11 | public void Uses_Double_Size() 12 | { 13 | var allocationStrategy = new DoubleSizeAllocationStrategy(); 14 | Assert.AreEqual(200, allocationStrategy.CalculateSize(100)); 15 | } 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /UniversalTest/UniversalTest.WindowsPhone/MainPage.xaml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /MarcelloDB/Index/AddressList.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using MarcelloDB.Helpers; 5 | 6 | namespace MarcelloDB.Index 7 | { 8 | internal class AddressList : ChangeTrackingList 9 | { 10 | internal List Addresses 11 | { 12 | get 13 | { 14 | return Items; 15 | } 16 | } 17 | 18 | internal void SetAddresses(List addresses) 19 | { 20 | base.Items = addresses; 21 | } 22 | } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /MarcelloDB/Index/EntryList.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using MarcelloDB.Helpers; 3 | using System.Collections.Generic; 4 | 5 | namespace MarcelloDB.Index 6 | { 7 | 8 | internal class EntryList : ChangeTrackingList> 9 | { 10 | public List> Entries 11 | { 12 | get 13 | { 14 | return base.Items; 15 | } 16 | } 17 | 18 | internal void SetEntries(List> entries) 19 | { 20 | base.Items = entries; 21 | } 22 | } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /MarcelloDB/Storage/StreamActors/StreamActor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MarcelloDB.Storage.StreamActors 4 | { 5 | internal class StreamActor : SessionBoundObject 6 | { 7 | protected string StreamName {get;set;} 8 | 9 | internal StreamActor(Session session, string streamName) : base(session) 10 | { 11 | this.StreamName = streamName; 12 | } 13 | 14 | protected IStorageStream GetStream() 15 | { 16 | return this.Session.StreamProvider.GetStream(this.StreamName); 17 | } 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /MarcelloDB/Records/CollectionMetaDataT.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Marcello 4 | { 5 | internal class CollectionMetaData 6 | { 7 | Marcello Session { get; set; } 8 | 9 | internal CollectionMetaData (Marcello session) 10 | { 11 | Session = session; 12 | } 13 | 14 | internal CollectionMetaDataRecord GetRecord() 15 | { 16 | return new CollectionMetaDataRecord(); 17 | } 18 | 19 | internal void Update(CollectionMetaDataRecord record) 20 | { 21 | 22 | } 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /docs/upgrade05.md: -------------------------------------------------------------------------------- 1 | #Upgrading to 0.5 2 | ~~Code compatible with 0.4 is expected to compile and run without modifications with 0.5.~~ 3 | 4 | ##Breaking changes 5 | There is a breaking change in code between 0.4 and 0.5. 6 | 7 | `IndexDefinition.Find` has been renamend to `IndexDefinition.Equals`. 8 | 9 | ##Data compatibility 10 | Data from 0.4 remains readable, but existing indexes are no longer readable so they will have to be rebuilt. 11 | 12 | Copying all objects to a new collection will do the trick. It is even better to copy the data to a new collection-file and delete the previous one to save space. -------------------------------------------------------------------------------- /MarcelloDB/Storage/StreamActors/JournalledReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MarcelloDB.Storage.StreamActors 4 | { 5 | internal class JournalledReader : Reader 6 | { 7 | internal JournalledReader(Session session, string collectionName) 8 | : base(session, collectionName) 9 | { 10 | } 11 | 12 | internal override byte[] Read (long address, int length) 13 | { 14 | var readBytes = base.Read (address, length); 15 | Session.Journal.ApplyToData(this.StreamName, address, readBytes); 16 | return readBytes; 17 | } 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /docs/persisting.md: -------------------------------------------------------------------------------- 1 | ##Persisting objects 2 | 3 | Once you have a collection, you can save your objects using the Persist method. 4 | 5 | ```cs 6 | var book = new Book(){ bookId = "123", Title = "The Girl With The Dragon Tattoo" }; 7 | bookCollection.Persist(book); 8 | ``` 9 | 10 | Persist inserts if there is no object associated with the ID. Otherwise it will perform an update. 11 | 12 | If you are using an index definition, the indexes will be updated. 13 | 14 | ##Destroying objects 15 | 16 | The Destroy method will remove the object with the given ID from the collection. 17 | 18 | ```cs 19 | bookCollection.Destroy("123"); 20 | ``` 21 | -------------------------------------------------------------------------------- /MarcelloDB/Index/EmptyRecordIndex.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using MarcelloDB.Records; 3 | using MarcelloDB.AllocationStrategies; 4 | using MarcelloDB.Index.BTree; 5 | using MarcelloDB.Serialization; 6 | 7 | namespace MarcelloDB.Index 8 | { 9 | internal class EmptyRecordIndex : RecordIndex 10 | { 11 | 12 | internal EmptyRecordIndex( 13 | Session session, 14 | IRecordManager recordManager, 15 | string indexName, 16 | IObjectSerializer> serializer 17 | ):base(session, recordManager, indexName, serializer) 18 | {} 19 | } 20 | } 21 | 22 | -------------------------------------------------------------------------------- /MarcelloDB.BenchmarkTool/Extensions/ListExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace MarcelloDB.BenchmarkTool.Extensions 5 | { 6 | public static class ListExtensions 7 | { 8 | public static void Shuffle(this IList list) 9 | { 10 | Random rng = new Random(); 11 | int n = list.Count; 12 | while (n > 1) { 13 | n--; 14 | int k = rng.Next(n + 1); 15 | T value = list[k]; 16 | list[k] = list[n]; 17 | list[n] = value; 18 | } 19 | } 20 | } 21 | } 22 | 23 | -------------------------------------------------------------------------------- /MarcelloDB.Test/TestStream/TestPlatform.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using MarcelloDB.Platform; 3 | using System.Collections.Generic; 4 | 5 | namespace MarcelloDB.Test 6 | { 7 | public class TestPlatform : IPlatform 8 | { 9 | Dictionary _streams = new Dictionary(); 10 | public MarcelloDB.Storage.IStorageStreamProvider CreateStorageStreamProvider(string rootPath) 11 | { 12 | if (!_streams.ContainsKey(rootPath)) 13 | { 14 | _streams.Add(rootPath, new InMemoryStreamProvider()); 15 | } 16 | return _streams[rootPath]; 17 | } 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /MarcelloDB/Index/Entry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MarcelloDB.Index 4 | { 5 | internal class Entry : IEquatable> 6 | { 7 | TK _key; 8 | public TK Key 9 | { 10 | get { return _key; } 11 | set 12 | { 13 | if (value == null) 14 | { 15 | throw new Exception("PANIC: Entry keys cannot be null"); 16 | } 17 | _key = value; 18 | } 19 | } 20 | 21 | public Int64 Pointer { get; set; } 22 | 23 | public bool Equals(Entry other) 24 | { 25 | return this.Key.Equals(other.Key) && this.Pointer.Equals(other.Pointer); 26 | } 27 | } 28 | } 29 | 30 | -------------------------------------------------------------------------------- /docs/template/couscous.yml: -------------------------------------------------------------------------------- 1 | template: 2 | directory: . 3 | 4 | github: 5 | user: CouscousPHP 6 | repo: Template-Dark 7 | 8 | title: Couscous template 9 | subTitle: The dark template for Couscous. 10 | 11 | # The left menu bar 12 | menu: 13 | sections: 14 | main: 15 | name: Main documentation 16 | items: 17 | home: 18 | text: Home page 19 | # You can use relative urls 20 | relativeUrl: doc/faq.html 21 | foo: 22 | text: Another link 23 | # Or absolute urls 24 | absoluteUrl: https://example.com 25 | other: 26 | name: Other topics 27 | items: 28 | -------------------------------------------------------------------------------- /MarcelloDB/Collections/Scopes/Descending.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using MarcelloDB.Records; 3 | using MarcelloDB.Collections; 4 | 5 | namespace MarcelloDB.Collections.Scopes 6 | { 7 | public class Descending : BaseScope 8 | { 9 | BaseScope OriginalScope { get; set; } 10 | 11 | internal Descending(BaseScope originalScope) 12 | { 13 | this.OriginalScope = originalScope; 14 | } 15 | 16 | internal override CollectionEnumerator> BuildEnumerator(bool descending) 17 | { 18 | return OriginalScope.BuildEnumerator(true); //force descending enumerator 19 | } 20 | } 21 | } 22 | 23 | -------------------------------------------------------------------------------- /MarcelloDB.BenchmarkTool/Benchmarks/RandomIDsBulkInsert.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using MarcelloDB.BenchmarkTool.DataClasses; 3 | 4 | namespace MarcelloDB.BenchmarkTool.Benchmarks 5 | { 6 | public class RandomIDsBulkInsert : Base 7 | { 8 | int _objectCount; 9 | 10 | public RandomIDsBulkInsert(int objectCount) 11 | { 12 | _objectCount = objectCount; 13 | } 14 | 15 | protected override void OnRun() 16 | { 17 | var r = new Random(); 18 | for (int i = 1; i < _objectCount; i++) 19 | { 20 | var person = Person.BuildRandom(); 21 | person.ID = r.Next(); 22 | this.Collection.Persist(person); 23 | } 24 | } 25 | } 26 | } 27 | 28 | -------------------------------------------------------------------------------- /docs/indexing/indexed_list.md: -------------------------------------------------------------------------------- 1 | #Indexing a list of fields with ```IndexedList``` 2 | MarcelloDB allows multiple index entries per object in the same index. 3 | 4 | Let's say for instance you have a ```Book``` class which has a property ```List Tags``` 5 | 6 | Using an ```IndexedList``` allows you to index every tag in the Tags property. 7 | 8 | ##Custom Value 9 | If indexing a property is not enough, custom indexes can be defined. 10 | In this case you also define a property like above, except you return an instance of IndexedValue, created with a function that returns the value to be indexed. 11 | 12 | ```cs 13 | class BookIndexDefinition : IndexDefinition 14 | { 15 | public IndexedList Tags 16 | { 17 | get { 18 | return IndexedList((o) => o.Tags); 19 | } 20 | } 21 | } 22 | ``` -------------------------------------------------------------------------------- /MarcelloDB.BenchmarkTool/Benchmarks/SequentialIDsBulkInsert.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using MarcelloDB.BenchmarkTool.DataClasses; 4 | 5 | namespace MarcelloDB.BenchmarkTool.Benchmarks 6 | { 7 | public class SequentialIDsBulkInsert : Base 8 | { 9 | 10 | int _objectCount; 11 | 12 | public SequentialIDsBulkInsert(int objectCount) 13 | { 14 | _objectCount = objectCount; 15 | } 16 | 17 | protected override void OnRun() 18 | { 19 | for (int i = 1; i < _objectCount; i++) 20 | { 21 | var person = Person.BuildRandom(); 22 | person.ID = i; 23 | this.Collection.Persist(person); 24 | } 25 | } 26 | } 27 | } 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /MarcelloDB.uwp/Platform.cs: -------------------------------------------------------------------------------- 1 | using MarcelloDB.Platform; 2 | using MarcelloDB.Storage; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using Windows.Storage; 9 | 10 | namespace MarcelloDB.uwp 11 | { 12 | public class Platform : IPlatform 13 | { 14 | public IStorageStreamProvider CreateStorageStreamProvider(string rootPath) 15 | { 16 | return new FileStorageStreamProvider(GetFolderForPath(rootPath)); 17 | } 18 | 19 | StorageFolder GetFolderForPath(string path) 20 | { 21 | var getFolderTask = StorageFolder.GetFolderFromPathAsync(path).AsTask(); 22 | getFolderTask.ConfigureAwait(false); 23 | getFolderTask.Wait(); 24 | return getFolderTask.Result; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /MarcelloDB/Serialization/JsonSerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json; 3 | 4 | namespace MarcelloDB.Serialization 5 | { 6 | internal class JsonSerializer : IObjectSerializer 7 | { 8 | public JsonSerializer () 9 | { 10 | } 11 | 12 | #region IObjectSerializer implementation 13 | 14 | public byte[] Serialize (T obj) 15 | { 16 | var json = JsonConvert.SerializeObject(obj); 17 | return System.Text.Encoding.UTF8.GetBytes(json.ToCharArray()); 18 | } 19 | 20 | public T Deserialize (byte[] bytes) 21 | { 22 | string json = ""; 23 | json = System.Text.Encoding.UTF8.GetString(bytes, 0, bytes.Length); 24 | return JsonConvert.DeserializeObject(json); 25 | } 26 | 27 | #endregion 28 | } 29 | } 30 | 31 | -------------------------------------------------------------------------------- /MarcelloDB/Collections/Scopes/All.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using MarcelloDB.Records; 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | using MarcelloDB.Index.BTree; 6 | 7 | namespace MarcelloDB.Collections.Scopes 8 | { 9 | public class All : BaseScope 10 | { 11 | BaseIndexedValue IndexedValue { get; set; } 12 | 13 | internal All(BaseIndexedValue indexedValue) 14 | { 15 | this.IndexedValue = indexedValue; 16 | 17 | } 18 | 19 | override internal CollectionEnumerator> BuildEnumerator(bool descending) 20 | { 21 | return this.IndexedValue 22 | .BuildEnumerator(new BTreeWalkerRange>[]{null}, descending); 23 | } 24 | } 25 | } 26 | 27 | -------------------------------------------------------------------------------- /MarcelloDB.W81/Platform.cs: -------------------------------------------------------------------------------- 1 | using MarcelloDB.Collections; 2 | using MarcelloDB.Platform; 3 | using MarcelloDB.Storage; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using Windows.Storage; 10 | 11 | namespace MarcelloDB.W81 12 | { 13 | public class Platform : IPlatform 14 | { 15 | public IStorageStreamProvider CreateStorageStreamProvider(string rootPath) 16 | { 17 | return new FileStorageStreamProvider(GetFolderForPath(rootPath)); 18 | } 19 | 20 | StorageFolder GetFolderForPath(string path) 21 | { 22 | var getFolderTask = StorageFolder.GetFolderFromPathAsync(path).AsTask(); 23 | getFolderTask.ConfigureAwait(false); 24 | getFolderTask.Wait(); 25 | return getFolderTask.Result; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /MarcelloDB.BenchmarkTool/DataClasses/Address.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MarcelloDB.BenchmarkTool.DataClasses 4 | { 5 | public class Address 6 | { 7 | static Random _random = new Random(); 8 | 9 | public string Street { get; set; } 10 | public string Number { get; set; } 11 | public int ZipCode { get; set; } 12 | public string City { get; set; } 13 | public string Country { get; set; } 14 | 15 | public static Address BuildRandom() 16 | { 17 | return new Address{ 18 | Street = "Street " + _random.Next().ToString(), 19 | Number = "Number " + _random.Next().ToString(), 20 | ZipCode = _random.Next() % 10000, 21 | City = "City " + _random.Next().ToString(), 22 | Country = "Country " + _random.Next().ToString() 23 | }; 24 | } 25 | } 26 | } 27 | 28 | -------------------------------------------------------------------------------- /MarcelloDB/Serialization/ValueSerializers/ByteSerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MarcelloDB.Serialization.ValueSerializers 4 | { 5 | class ByteSerializer : ValueSerializer 6 | { 7 | internal override Byte ReadValue(BinaryFormatter formatter) 8 | { 9 | return formatter.ReadByte(); 10 | } 11 | internal override void WriteValue(BinaryFormatter formatter, Byte value) 12 | { 13 | formatter.WriteByte(value); 14 | } 15 | } 16 | 17 | class NullableByteSerializer : ValueSerializer 18 | { 19 | internal override Byte? ReadValue(BinaryFormatter formatter) 20 | { 21 | return formatter.ReadNullableByte(); 22 | } 23 | internal override void WriteValue(BinaryFormatter formatter, Byte? value) 24 | { 25 | formatter.WriteNullableByte(value); 26 | } 27 | } 28 | } 29 | 30 | -------------------------------------------------------------------------------- /MarcelloDB/Serialization/ValueSerializers/BooleanSerializers.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MarcelloDB.Serialization.ValueSerializers 4 | { 5 | class BooleanSerializer : ValueSerializer 6 | { 7 | internal override Boolean ReadValue(BinaryFormatter formatter) 8 | { 9 | return formatter.ReadBool(); 10 | } 11 | internal override void WriteValue(BinaryFormatter formatter, Boolean value) 12 | { 13 | formatter.WriteBool(value); 14 | } 15 | } 16 | 17 | class NullableBooleanSerializer : ValueSerializer 18 | { 19 | internal override Boolean? ReadValue(BinaryFormatter formatter) 20 | { 21 | return formatter.ReadNullableBool(); 22 | } 23 | internal override void WriteValue(BinaryFormatter formatter, Boolean? value) 24 | { 25 | formatter.WriteNullableBool(value); 26 | } 27 | } 28 | } 29 | 30 | -------------------------------------------------------------------------------- /UniversalTest/UniversalTest.Shared/UniversalTest.Shared.projitems: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(MSBuildAllProjects);$(MSBuildThisFileFullPath) 5 | true 6 | 268d5135-6f43-4de0-8fda-0053c90d2c93 7 | 8 | 9 | UniversalTest 10 | 11 | 12 | 13 | Designer 14 | 15 | 16 | App.xaml 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /MarcelloDB/Transactions/JournalEntry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace MarcelloDB.Transactions 5 | { 6 | internal static class TimeStamp 7 | { 8 | static long _lastStamp; 9 | 10 | internal static long Next(){ 11 | return ++_lastStamp; 12 | } 13 | } 14 | 15 | internal class TransactionJournal 16 | { 17 | public List Entries { get; set; } 18 | 19 | public TransactionJournal() 20 | { 21 | this.Entries = new List(); 22 | } 23 | } 24 | 25 | internal class JournalEntry 26 | { 27 | public long Stamp { get; set; } 28 | 29 | public string StreamName { get; set; } 30 | 31 | public Int64 Address { get; set; } 32 | 33 | public byte[] Data { get; set; } 34 | 35 | public JournalEntry(){ 36 | this.Stamp = TimeStamp.Next(); 37 | } 38 | } 39 | } 40 | 41 | -------------------------------------------------------------------------------- /UniversalTest/UniversalTest.Windows/MainPage.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Runtime.InteropServices.WindowsRuntime; 6 | using Windows.Foundation; 7 | using Windows.Foundation.Collections; 8 | using Windows.UI.Xaml; 9 | using Windows.UI.Xaml.Controls; 10 | using Windows.UI.Xaml.Controls.Primitives; 11 | using Windows.UI.Xaml.Data; 12 | using Windows.UI.Xaml.Input; 13 | using Windows.UI.Xaml.Media; 14 | using Windows.UI.Xaml.Navigation; 15 | 16 | // The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238 17 | 18 | namespace UniversalTest 19 | { 20 | /// 21 | /// An empty page that can be used on its own or navigated to within a Frame. 22 | /// 23 | public sealed partial class MainPage : Page 24 | { 25 | public MainPage() 26 | { 27 | this.InitializeComponent(); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /MarcelloDB/Serialization/ValueSerializers/DateTimeSerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MarcelloDB.Serialization.ValueSerializers 4 | { 5 | class DateTimeSerializer : ValueSerializer 6 | { 7 | internal override DateTime ReadValue(BinaryFormatter formatter) 8 | { 9 | return formatter.ReadDateTime(); 10 | } 11 | internal override void WriteValue(BinaryFormatter formatter, DateTime value) 12 | { 13 | formatter.WriteDateTime(value); 14 | } 15 | } 16 | 17 | class NullableDateTimeSerializer : ValueSerializer 18 | { 19 | internal override DateTime? ReadValue(BinaryFormatter formatter) 20 | { 21 | return formatter.ReadNullableDateTime(); 22 | } 23 | internal override void WriteValue(BinaryFormatter formatter, DateTime? value) 24 | { 25 | formatter.WriteNullableDateTime(value); 26 | } 27 | } 28 | } 29 | 30 | -------------------------------------------------------------------------------- /MarcelloDB/Records/EmptyRecordIndexKey.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MarcelloDB.Records 4 | { 5 | internal class EmptyRecordIndexKey : IComparable, IEquatable 6 | { 7 | public Int64 A { get; set; } //Address, abbreviated to save storage 8 | public Int32 S { get; set; } //Size 9 | 10 | #region IComparable implementation 11 | 12 | public int CompareTo(object obj) 13 | { 14 | var other = (EmptyRecordIndexKey)obj; 15 | var sizeCompared = S.CompareTo(other.S); 16 | if(sizeCompared == 0) 17 | { 18 | return A.CompareTo(other.A); 19 | } 20 | return sizeCompared; 21 | } 22 | 23 | #endregion 24 | 25 | #region IEquatable implementation 26 | 27 | public bool Equals(EmptyRecordIndexKey other) 28 | { 29 | return this.A.Equals(other.A) && this.S.Equals(other.S); 30 | } 31 | 32 | #endregion 33 | } 34 | } 35 | 36 | -------------------------------------------------------------------------------- /docs/upgrade04.md: -------------------------------------------------------------------------------- 1 | #Upgrading to 0.4 2 | Version 0.4 contains a refactor which causes data files created with older version to be unreadable. 3 | 4 | There is also a code-level incompatibility. 5 | 6 | From 0.4 on, it is required to specify the type of your ID property. 7 | Also an Object-To-ID mapping function should be provided. 8 | 9 | Before 0.4: 10 | ```cs 11 | session['products'].Collection("My-Products-Collection"); 12 | ```` 13 | 14 | From 0.4: 15 | ```cs 16 | session['products'].Collection("My-Products-Collection", p => p.ID); 17 | ```` 18 | 19 | When using an IndexDefinition: 20 | Before 0.4: 21 | ```cs 22 | session['products'].Collection("My-Products-Collection"); 23 | ```` 24 | 25 | From 0.4: 26 | ```cs 27 | session['products'].Collection("My-Products-Collection", p => p.ID); 28 | ```` 29 | 30 | You are now free to name your ID property as you wish. 31 | 32 | The ID attribute has also been removed as there was no use for it anymore. 33 | 34 | -------------------------------------------------------------------------------- /MarcelloDB.Test/Classes/Location.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MarcelloDB.Test 4 | { 5 | public class Location 6 | { 7 | public string ID { get; set; } 8 | public string Name { get; set; } 9 | public string Address { get; set; } 10 | 11 | public static Location Harrods 12 | { 13 | get 14 | { 15 | return new Location 16 | { 17 | ID = "1a1a", 18 | Name = "Harrods", 19 | Address = "87-135 Brompton Road, Knightsbridge SW1X 7XL" 20 | }; 21 | } 22 | } 23 | 24 | public static Location MandS 25 | { 26 | get 27 | { 28 | return new Location 29 | { 30 | ID = "2b2b", 31 | Name = "Marks & Spencer", 32 | Address = "458 Oxford Street, W1C 1AP" 33 | }; 34 | } 35 | } 36 | } 37 | } 38 | 39 | -------------------------------------------------------------------------------- /Package/MarcelloDB.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MarcelloDB 5 | MarcelloDB, Mobile Object Database. 6 | 1.0.9 7 | Mark Meeus 8 | Mark Meeus 9 | http://marcellodb.org 10 | false 11 | NoSql for Xamarin and .net. Persists plain C# objects, including child objects, lists and collections. Supported on Xamarin (Android and iOS) and Windows (8.1-10 and Phone 8.1-10). 12 | 1.0.9. Fixes a race condition with collection creation. 13 | Copyright 2015-2020 14 | Database NoSql Mobile Xamarin Windows Android iOS 15 | http://markmeeus.github.io/MarcelloDB/images/logo/icon_blue_512x512.png 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /MarcelloDB.Test/Records/RecordHeaderTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using MarcelloDB.Records; 3 | using NUnit.Framework; 4 | 5 | namespace MarcelloDB.Test.Records 6 | { 7 | [TestFixture] 8 | public class RecordHeaderTest 9 | { 10 | [Test] 11 | public void Serializes_To_And_From_Bytes() 12 | { 13 | var header = RecordHeader.New(); 14 | header.HeaderVersion = 1; 15 | header.DataSize = 3; 16 | header.AllocatedDataSize = 4; 17 | Int64 address = 5; 18 | 19 | var bytes = header.AsBytes(); 20 | var loadedHeader = RecordHeader.FromBytes(address, bytes); 21 | 22 | Assert.AreEqual(header.HeaderVersion, loadedHeader.HeaderVersion, "HeaderVersion"); 23 | Assert.AreEqual(header.DataSize, loadedHeader.DataSize, "DataSize"); 24 | Assert.AreEqual(header.AllocatedDataSize, loadedHeader.AllocatedDataSize, "AllocatedSize"); 25 | Assert.AreEqual(address, loadedHeader.Address, "Address"); 26 | } 27 | } 28 | } 29 | 30 | -------------------------------------------------------------------------------- /UniversalTest/UniversalTest.Shared/UniversalTest.Shared.shproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 268d5135-6f43-4de0-8fda-0053c90d2c93 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /docs/transactions.md: -------------------------------------------------------------------------------- 1 | #Transactions 2 | 3 | To avoid data corruption, all changes are written to a write ahead journal and applied as a single atomic and durable action. 4 | MarcelloDB does this for calls to Persist and Destroy automatically. 5 | 6 | You can extend the transaction to make it span multiple data mutations. 7 | 8 | A transaction runs on a session and can include changes in multiple collections from multiple collection files. 9 | 10 | > Warning: only collections obtained from that session will be included in the transaction. If you start to mix multiple sessions, you're on your own. 11 | 12 | 13 | ```cs 14 | session.Transaction(() => { 15 | articleCollection.Persist(article); 16 | clientCollection.Persist(client); 17 | projectCollection.Destroy(project); 18 | }); 19 | ``` 20 | 21 | Transactions roll back when an exception occurs within the block. 22 | ```cs 23 | session.Transaction(() => { 24 | articleCollection.Persist(article); 25 | clientCollection.Persist(client); 26 | projectCollection.Destroy(project); 27 | throw new Exception("Nothing happened"); 28 | }); 29 | ``` 30 | -------------------------------------------------------------------------------- /MarcelloDB/Storage/StorageEngine.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using MarcelloDB; 3 | using MarcelloDB.Storage.StreamActors; 4 | using MarcelloDB.Transactions; 5 | 6 | namespace MarcelloDB.Storage 7 | { 8 | internal class StorageEngine : SessionBoundObject 9 | { 10 | 11 | string StreamName { get; set; } 12 | 13 | public StorageEngine(Session session,string streamName) : base(session) 14 | { 15 | this.StreamName = streamName; 16 | } 17 | 18 | internal byte[] Read(long address, int length) 19 | { 20 | return Reader().Read(address, length); 21 | } 22 | 23 | internal void Write(long address, byte[] bytes) 24 | { 25 | Writer().Write(address, bytes); 26 | } 27 | 28 | #region reader/writer factories 29 | Writer Writer() 30 | { 31 | return new JournalledWriter(this.Session, this.StreamName); 32 | } 33 | 34 | Reader Reader() 35 | { 36 | return new JournalledReader(this.Session, this.StreamName); 37 | } 38 | #endregion 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /docs/template/LICENSE: -------------------------------------------------------------------------------- 1 | Couscous 2 | 3 | Copyright (C) Matthieu Napoli 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 6 | associated documentation files (the "Software"), to deal in the Software without restriction, 7 | including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 9 | subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all copies or substantial 12 | portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 15 | NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 16 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 17 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 18 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /MarcelloDB.netfx/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | 4 | // Information about this assembly is defined by the following attributes. 5 | // Change them to the values specific to your project. 6 | 7 | [assembly: AssemblyTitle ("MarcelloDB.netfx")] 8 | [assembly: AssemblyDescription ("")] 9 | [assembly: AssemblyConfiguration ("")] 10 | [assembly: AssemblyCompany ("")] 11 | [assembly: AssemblyProduct ("")] 12 | [assembly: AssemblyCopyright ("Mark Meeus")] 13 | [assembly: AssemblyTrademark ("")] 14 | [assembly: AssemblyCulture ("")] 15 | 16 | // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". 17 | // The form "{Major}.{Minor}.*" will automatically update the build and revision, 18 | // and "{Major}.{Minor}.{Build}.*" will update just the revision. 19 | 20 | [assembly: AssemblyVersion ("1.0.9.*")] 21 | 22 | // The following attributes are used to specify the signing key for the assembly, 23 | // if desired. See the Mono documentation for more information about signing. 24 | 25 | //[assembly: AssemblyDelaySign(false)] 26 | //[assembly: AssemblyKeyFile("")] 27 | 28 | -------------------------------------------------------------------------------- /MarcelloDB.BenchmarkTool/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | 4 | // Information about this assembly is defined by the following attributes. 5 | // Change them to the values specific to your project. 6 | 7 | [assembly: AssemblyTitle("MarcelloDB.BenchmarkTool")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("")] 12 | [assembly: AssemblyCopyright("markmeeus")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". 17 | // The form "{Major}.{Minor}.*" will automatically update the build and revision, 18 | // and "{Major}.{Minor}.{Build}.*" will update just the revision. 19 | 20 | [assembly: AssemblyVersion("1.0.*")] 21 | 22 | // The following attributes are used to specify the signing key for the assembly, 23 | // if desired. See the Mono documentation for more information about signing. 24 | 25 | //[assembly: AssemblyDelaySign(false)] 26 | //[assembly: AssemblyKeyFile("")] 27 | 28 | -------------------------------------------------------------------------------- /MarcelloDB/AllocationStrategies/AllocationStrategyResolver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using MarcelloDB.Index; 3 | using MarcelloDB.Records; 4 | 5 | namespace MarcelloDB.AllocationStrategies 6 | { 7 | internal class AllocationStrategyResolver 8 | { 9 | internal IAllocationStrategy StrategyFor(Node emptyRecordIndexNode) 10 | { 11 | return new ExactSizeAllocationStrategy(); 12 | } 13 | 14 | internal IAllocationStrategy StrategyFor(Node node) 15 | { 16 | if (node.GetType () == typeof (Node)) { 17 | return StrategyFor ((Node)(object)node); 18 | } 19 | return new PredictiveBTreeNodeAllocationStrategy(node); 20 | } 21 | 22 | internal IAllocationStrategy StrategyFor(object obj) 23 | { 24 | if(obj.GetType() == typeof(Node)) 25 | { 26 | return StrategyFor((Node)obj); 27 | } 28 | return new DoubleSizeAllocationStrategy(); 29 | } 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /docs/longterm.md: -------------------------------------------------------------------------------- 1 | ## A few long-term ideas. 2 | 3 | Some ideas for MarcelloDB after 1.0: 4 | 5 | ###Pub-Sub 6 | 7 | After deploying a pubsub server (to be developed). 8 | One would be able to do this: 9 | 10 | ```cs 11 | //on the publishing side 12 | session['products.dat'].Publish("marcellodb://user:pass@someserver.tld/products.dat"); 13 | ``` 14 | 15 | ```cs 16 | //on the receiving side 17 | session['products.dat'].Subscribe("marcellodb://user:pass@someserver.tld/products.dat"); 18 | ``` 19 | Supports 1 Publisher, and many Subscribers per collection-file. 20 | 21 | 22 | ###Full Text indexes 23 | 24 | ```cs 25 | class BookIndexDefinition : IndexDefinition 26 | { 27 | public FullTextIndexedValue AllFullText { get;set; } 28 | } 29 | ``` 30 | 31 | The FullTextIndexedValue will create a full-text index for every String. 32 | When using the auto-implemented get-set, it will use the instance of the persisted object. 33 | 34 | It will also be possible to implement a mapper if only a specific set of properties needs to be indexed. 35 | 36 | Find will return a FullTextResult: 37 | ```cs 38 | bookCollection.AllFullText.Find("for dummies"); 39 | ``` -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) [2015] [Mark Meeus] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MarcelloDB.BenchmarkTool/Benchmarks/SequentialDestroy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using MarcelloDB.BenchmarkTool.DataClasses; 3 | using System.Linq; 4 | using System.Collections.Generic; 5 | 6 | namespace MarcelloDB.BenchmarkTool.Benchmarks 7 | { 8 | public class SequentialDestroy : Base 9 | { 10 | int _objectCount ; 11 | 12 | List _toDestroy; 13 | 14 | public SequentialDestroy(int objectCount) 15 | { 16 | _objectCount = objectCount; 17 | } 18 | 19 | protected override void OnSetup() 20 | { 21 | for (int i = 0; i < _objectCount; i++) 22 | { 23 | var person = Person.BuildRandom(); 24 | person.ID = i; 25 | this.Collection.Persist(person); 26 | } 27 | 28 | _toDestroy = this.Collection.All.ToList(); 29 | 30 | base.OnSetup(); 31 | } 32 | 33 | protected override void OnRun() 34 | { 35 | 36 | foreach (var p in _toDestroy) 37 | { 38 | this.Collection.Destroy(p.ID); 39 | } 40 | } 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /MarcelloDB/Serialization/ValueSerializers/ValueWithAddressIndexKeySerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using MarcelloDB.Records; 3 | 4 | namespace MarcelloDB.Serialization.ValueSerializers 5 | { 6 | class ValueWithAddressSerializer : ValueSerializer> 7 | { 8 | ValueSerializer ValueSerializer { get; set; } 9 | 10 | public ValueWithAddressSerializer(ValueSerializer valueSerializer){ 11 | this.ValueSerializer = valueSerializer; 12 | } 13 | 14 | internal override void WriteValue(BinaryFormatter formatter, ValueWithAddressIndexKey value) 15 | { 16 | formatter.WriteInt64(value.A); 17 | this.ValueSerializer.WriteValue(formatter, value.V); 18 | } 19 | 20 | internal override ValueWithAddressIndexKey ReadValue(BinaryFormatter formatter) 21 | { 22 | var result = new ValueWithAddressIndexKey(); 23 | result.A = formatter.ReadInt64(); 24 | result.V = this.ValueSerializer.ReadValue(formatter); 25 | return result; 26 | } 27 | } 28 | } 29 | 30 | -------------------------------------------------------------------------------- /MarcelloDB/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | 4 | // Information about this assembly is defined by the following attributes. 5 | // Change them to the values specific to your project. 6 | 7 | [assembly: AssemblyTitle ("MarcelloDB")] 8 | [assembly: AssemblyDescription ("")] 9 | [assembly: AssemblyConfiguration ("")] 10 | [assembly: AssemblyCompany ("")] 11 | [assembly: AssemblyProduct ("")] 12 | [assembly: AssemblyCopyright ("Mark Meeus")] 13 | [assembly: AssemblyTrademark ("")] 14 | [assembly: AssemblyCulture ("")] 15 | 16 | // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". 17 | // The form "{Major}.{Minor}.*" will automatically update the build and revision, 18 | // and "{Major}.{Minor}.{Build}.*" will update just the revision. 19 | 20 | [assembly: AssemblyVersion("1.0.9.*")] 21 | 22 | // The following attributes are used to specify the signing key for the assembly, 23 | // if desired. See the Mono documentation for more information about signing. 24 | 25 | //[assembly: AssemblyDelaySign(false)] 26 | //[assembly: AssemblyKeyFile("")] 27 | 28 | [assembly :InternalsVisibleTo("MarcelloDB.Test")] -------------------------------------------------------------------------------- /MarcelloDB.BenchmarkTool/Benchmarks/RandomRead.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using MarcelloDB.BenchmarkTool.DataClasses; 3 | 4 | namespace MarcelloDB.BenchmarkTool.Benchmarks 5 | { 6 | public class RandomRead : Base 7 | { 8 | int _objectCount ; 9 | 10 | public RandomRead(int objectCount) 11 | { 12 | _objectCount = objectCount; 13 | } 14 | 15 | protected override void OnSetup() 16 | { 17 | for (int i = 0; i < _objectCount; i++) 18 | { 19 | var person = Person.BuildRandom(); 20 | person.ID = i; 21 | this.Collection.Persist(person); 22 | } 23 | base.OnSetup(); 24 | } 25 | 26 | protected override void OnRun() 27 | { 28 | var r = new Random(); 29 | for (int i = 0; i < _objectCount; i++) 30 | { 31 | var o = this.Collection.Find(r.Next(_objectCount)); 32 | if(o == null){ 33 | throw new Exception("Object with Id " + i.ToString() + " not found"); 34 | } 35 | } 36 | } 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /MarcelloDB.uwp/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("MarcelloDB.uwp")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("MarcelloDB.uwp")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Version information for an assembly consists of the following four values: 18 | // 19 | // Major Version 20 | // Minor Version 21 | // Build Number 22 | // Revision 23 | // 24 | // You can specify all the values or you can default the Build and Revision Numbers 25 | // by using the '*' as shown below: 26 | // [assembly: AssemblyVersion("1.0.*")] 27 | [assembly: AssemblyVersion("1.0.9.0")] 28 | [assembly: AssemblyFileVersion("1.0.9.0")] 29 | [assembly: ComVisible(false)] -------------------------------------------------------------------------------- /UniversalTest/UniversalTest.Windows/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("UniversalTest.Windows")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("UniversalTest.Windows")] 13 | [assembly: AssemblyCopyright("Copyright © 2015")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Version information for an assembly consists of the following four values: 18 | // 19 | // Major Version 20 | // Minor Version 21 | // Build Number 22 | // Revision 23 | // 24 | // You can specify all the values or you can default the Build and Revision Numbers 25 | // by using the '*' as shown below: 26 | // [assembly: AssemblyVersion("1.0.*")] 27 | [assembly: AssemblyVersion("1.0.0.0")] 28 | [assembly: AssemblyFileVersion("1.0.0.0")] 29 | [assembly: ComVisible(false)] -------------------------------------------------------------------------------- /MarcelloDB.W81/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Resources; 2 | using System.Reflection; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | 6 | // General Information about an assembly is controlled through the following 7 | // set of attributes. Change these attribute values to modify the information 8 | // associated with an assembly. 9 | [assembly: AssemblyTitle("MarceloDB.uwp")] 10 | [assembly: AssemblyDescription("")] 11 | [assembly: AssemblyConfiguration("")] 12 | [assembly: AssemblyCompany("")] 13 | [assembly: AssemblyProduct("MarceloDB.uwp")] 14 | [assembly: AssemblyCopyright("Mark Meeus")] 15 | [assembly: AssemblyTrademark("")] 16 | [assembly: AssemblyCulture("")] 17 | [assembly: NeutralResourcesLanguage("en")] 18 | 19 | // Version information for an assembly consists of the following four values: 20 | // 21 | // Major Version 22 | // Minor Version 23 | // Build Number 24 | // Revision 25 | // 26 | // You can specify all the values or you can default the Build and Revision Numbers 27 | // by using the '*' as shown below: 28 | // [assembly: AssemblyVersion("1.0.*")] 29 | [assembly: AssemblyVersion("1.0.2.*")] 30 | [assembly: AssemblyFileVersion("1.0.2.*")] 31 | -------------------------------------------------------------------------------- /MarcelloDB/Collections/ObjectByIDEnumerator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections; 4 | 5 | namespace MarcelloDB.Collections 6 | { 7 | class ObjectByIDEnumerator : IEnumerable{ 8 | 9 | internal IEnumerable IDs { get; set; } 10 | 11 | internal Collection Collection { get; set; } 12 | 13 | internal ObjectByIDEnumerator(Collection collection, IEnumerable ids){ 14 | this.IDs = ids; 15 | this.Collection = collection; 16 | } 17 | 18 | #region IEnumerable implementation 19 | public IEnumerator GetEnumerator() 20 | { 21 | foreach (var id in this.IDs) 22 | { 23 | var obj = this.Collection.Find(id); 24 | if (obj != null) 25 | { 26 | yield return obj; 27 | } 28 | } 29 | } 30 | #endregion 31 | 32 | #region IEnumerable implementation 33 | 34 | IEnumerator IEnumerable.GetEnumerator() 35 | { 36 | return ((IEnumerable)this).GetEnumerator(); 37 | } 38 | #endregion 39 | } 40 | } 41 | 42 | -------------------------------------------------------------------------------- /MarcelloDB.BenchmarkTool/Benchmarks/RandomDestroy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using MarcelloDB.BenchmarkTool.DataClasses; 4 | using System.Linq; 5 | using MarcelloDB.BenchmarkTool.Extensions; 6 | 7 | namespace MarcelloDB.BenchmarkTool.Benchmarks 8 | { 9 | public class RandomDestroy : Base 10 | { 11 | int _objectCount ; 12 | 13 | List _toDestroy; 14 | 15 | public RandomDestroy(int objectCount) 16 | { 17 | _objectCount = objectCount; 18 | } 19 | 20 | protected override void OnSetup() 21 | { 22 | for (int i = 0; i < _objectCount; i++) 23 | { 24 | var person = Person.BuildRandom(); 25 | person.ID = i; 26 | this.Collection.Persist(person); 27 | } 28 | 29 | _toDestroy = this.Collection.All.ToList(); 30 | _toDestroy.Shuffle(); 31 | 32 | base.OnSetup(); 33 | } 34 | 35 | protected override void OnRun() 36 | { 37 | 38 | foreach (var p in _toDestroy) 39 | { 40 | this.Collection.Destroy(p.ID); 41 | } 42 | } 43 | } 44 | } 45 | 46 | -------------------------------------------------------------------------------- /MarcelloDB/Collections/IndexedValue.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Collections.Generic; 4 | using MarcelloDB.Records; 5 | using MarcelloDB.Serialization; 6 | using MarcelloDB.Index; 7 | using System.Collections; 8 | using MarcelloDB.Index.BTree; 9 | using MarcelloDB.Collections.Scopes; 10 | 11 | namespace MarcelloDB.Collections 12 | { 13 | public abstract class IndexedValue : SessionBoundObject 14 | { 15 | public IndexedValue(Session session) : base(session){} 16 | 17 | internal abstract IEnumerable GetKeys(object o, Int64 address); 18 | 19 | protected internal abstract void Register(object o, Int64 address); 20 | 21 | protected internal abstract void UnRegister(object o, Int64 address); 22 | 23 | protected internal string PropertyName { get; set; } 24 | 25 | protected internal abstract void EnsureIndex(); 26 | 27 | internal abstract void SetContext (Collection collection, 28 | Session session, 29 | RecordManager recordManager, 30 | object serializer, 31 | string propertyName); 32 | 33 | internal abstract object Build (); 34 | } 35 | } -------------------------------------------------------------------------------- /UniversalTest/UniversalTest.WindowsPhone/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("UniversalTest.WindowsPhone")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("UniversalTest.WindowsPhone")] 13 | [assembly: AssemblyCopyright("Copyright © 2015")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Version information for an assembly consists of the following four values: 18 | // 19 | // Major Version 20 | // Minor Version 21 | // Build Number 22 | // Revision 23 | // 24 | // You can specify all the values or you can default the Build and Revision Numbers 25 | // by using the '*' as shown below: 26 | // [assembly: AssemblyVersion("1.0.*")] 27 | [assembly: AssemblyVersion("1.0.0.0")] 28 | [assembly: AssemblyFileVersion("1.0.0.0")] 29 | [assembly: ComVisible(false)] -------------------------------------------------------------------------------- /MarcelloDB.BenchmarkTool/Benchmarks/EnumerateAll.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using MarcelloDB.BenchmarkTool.DataClasses; 3 | 4 | namespace MarcelloDB.BenchmarkTool.Benchmarks 5 | { 6 | public class EnumerateAll : Base 7 | { 8 | int _objectCount ; 9 | 10 | public EnumerateAll(int objectCount) 11 | { 12 | _objectCount = objectCount; 13 | } 14 | 15 | protected override void OnSetup() 16 | { 17 | for (int i = 0; i < _objectCount; i++) 18 | { 19 | var person = Person.BuildRandom(); 20 | person.ID = i; 21 | this.Collection.Persist(person); 22 | } 23 | base.OnSetup(); 24 | } 25 | 26 | protected override void OnRun() 27 | { 28 | var currentId = 0; 29 | foreach (var p in this.Collection.All) 30 | { 31 | if (currentId != p.ID) 32 | { 33 | throw new InvalidOperationException( 34 | string.Format("Expecting Id to be :{0} instead of {1}",currentId, p.ID)); 35 | } 36 | currentId ++; 37 | } 38 | } 39 | } 40 | } 41 | 42 | -------------------------------------------------------------------------------- /docs/template/images/logo/icon_white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 10 | 11 | 13 | 14 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /docs/template/images/logo/icon_blue.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 10 | 11 | 13 | 14 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | #Welcome to MarcelloDB's Documentation pages. 2 | 3 | MarcelloDB is an [open-source](https://github.com/markmeeus/marcellodb) embedded NoSql object-database for Xamarin and UWP (Windows Universal) apps. 4 | 5 | MarcelloDB saves entire C# object graphs, including child objects like lists and dictionaries. 6 | 7 | It's a native C# implementation, focussed on being light-weight and developer-friendly. 8 | 9 | MarcelloDB's core is a portable class library for Xamarin (iOS and Android), Windows(Phone) 8.1 and Windows 10. 10 | 11 | 12 | ###New to MarcelloDB? 13 | 14 | Read the [Quickstart Guide](quickstart.md) to get up to speed quickly. 15 | 16 | A lot of thought went into the API design, we hope you'll like it. 17 | 18 | If you have any questions, thoughts or remarks. Please open an issue on [github.](https://github.com/markmeeus/marcellodb/issues) 19 | 20 | ###Current Version: 21 | Current version is 1.0.9 22 | Although it's the first version, the community has been testing beta versions since october 2015. It is allready really stable. 23 | 24 | ##Need Support? 25 | If you want a direct support line to the developers behind this project, a Pro/Support package is available. 26 | Please contact mark.meeus@gmail.com for details. 27 | 28 | This package will also include [MarcelloDB.Encryption](encryption.html). 29 | -------------------------------------------------------------------------------- /MarcelloDB.Test/Regression/Issue32_OutOfMemoryOnSecondPersist.cs: -------------------------------------------------------------------------------- 1 | using MarcelloDB.Collections; 2 | using NUnit.Framework; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace MarcelloDB.Test.Regression.Issue32 10 | { 11 | public class Product 12 | { 13 | public Guid Id { get; set; } 14 | } 15 | 16 | [TestFixture] 17 | public class Issue32_OutOfMemoryOnSecondPersist 18 | { 19 | TestPlatform _platform; 20 | 21 | [SetUp] 22 | public void Setup() 23 | { 24 | _platform = new TestPlatform(); 25 | } 26 | 27 | [Test] 28 | public void Should_Not_Throw_On_Second_Persist() 29 | { 30 | SaveProduct(); 31 | Assert.DoesNotThrow(SaveProduct); 32 | } 33 | 34 | void SaveProduct() 35 | { 36 | using(var session = new Session(_platform, "/")) 37 | { 38 | var collectionFile = session["products"]; 39 | var products = collectionFile.Collection("products", p => p.Id); 40 | 41 | var product = new Product(); 42 | product.Id = Guid.NewGuid(); 43 | products.Persist(product); 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /MarcelloDB/Collections/IndexedValueTypes/UniqueIndexedValue.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using MarcelloDB.Collections; 3 | using System.Linq; 4 | using System.Collections.Generic; 5 | using MarcelloDB.Records; 6 | 7 | namespace MarcelloDB 8 | { 9 | public class UniqueIndexedValue : IndexedValue 10 | { 11 | public UniqueIndexedValue():base(){} 12 | 13 | internal UniqueIndexedValue( 14 | Func valueFunction, 15 | Func shouldIndexPredicate 16 | ):base( valueFunction, shouldIndexPredicate) 17 | { 18 | } 19 | 20 | internal static new UniqueIndexedValue Build() 21 | { 22 | return new UniqueIndexedValue(); 23 | } 24 | 25 | public TObj Find(TAttribute value){ 26 | 27 | return base.Equals(value).First(); 28 | } 29 | 30 | internal override IEnumerable GetKeys(object o, long address) 31 | { 32 | var value = base.ValueFunction((TObj)o).FirstOrDefault(); 33 | var indexKey = new ValueWithAddressIndexKey 34 | { 35 | V = value, 36 | A = 0 //Makes entries unique 37 | }; 38 | return new object[]{ indexKey }; 39 | } 40 | } 41 | 42 | } 43 | 44 | -------------------------------------------------------------------------------- /MarcelloDB.Test/Regression/Issue31_NullReferenceWhenPersistingWithNonID.cs: -------------------------------------------------------------------------------- 1 | using MarcelloDB.Collections; 2 | using NUnit.Framework; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | //https://github.com/markmeeus/MarcelloDB/issues/31 10 | namespace MarcelloDB.Test.Regression.Issue31 11 | { 12 | public class Product 13 | { 14 | public Guid Id { get; set; } 15 | } 16 | 17 | [TestFixture] 18 | public class Issue31_NullReferenceWhenPersistingWithNonID 19 | { 20 | 21 | Session _session; 22 | CollectionFile _collectionFile; 23 | Collection _products; 24 | TestPlatform _platform; 25 | 26 | [SetUp] 27 | public void Setup() 28 | { 29 | _platform = new TestPlatform(); 30 | _session = new Session(_platform, "/"); 31 | _collectionFile = _session["articles"]; 32 | _products = _collectionFile.Collection("products", p => p.Id); 33 | } 34 | 35 | [Test] 36 | public void Persist_With_Id_Property() 37 | { 38 | var entitiy = new Product(); 39 | entitiy.Id = Guid.NewGuid(); 40 | _products.Persist(entitiy); 41 | var foundEntity = _products.Find(entitiy.Id); 42 | Assert.AreEqual(entitiy.Id, foundEntity.Id); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /MarcelloDB/Serialization/BsonSerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json; 3 | using System.IO; 4 | using Newtonsoft.Json.Bson; 5 | 6 | namespace MarcelloDB.Serialization 7 | { 8 | internal class ObjectWrapper 9 | { 10 | public T O { get; set; } 11 | } 12 | 13 | 14 | internal class BsonSerializer : IObjectSerializer 15 | { 16 | JsonSerializer JsonSerializer { get; set; } 17 | 18 | public BsonSerializer () 19 | { 20 | this.JsonSerializer = GetSerializer(); 21 | } 22 | 23 | #region IObjectSerializer implementation 24 | 25 | public byte[] Serialize(T o) 26 | { 27 | var memoryStream = new MemoryStream(); 28 | var bsonWriter = new BsonWriter(memoryStream); 29 | 30 | this.JsonSerializer.Serialize(bsonWriter, new ObjectWrapper{O=o}); 31 | bsonWriter.Flush(); 32 | 33 | return memoryStream.ToArray(); 34 | } 35 | 36 | public T Deserialize (byte[] bytes) 37 | { 38 | var reader = new BsonReader( 39 | new MemoryStream(bytes) 40 | ); 41 | 42 | return this.JsonSerializer.Deserialize>(reader).O; 43 | } 44 | #endregion 45 | 46 | JsonSerializer GetSerializer(){ 47 | return new JsonSerializer {TypeNameHandling = TypeNameHandling.Auto}; 48 | } 49 | } 50 | } 51 | 52 | -------------------------------------------------------------------------------- /MarcelloDB.Test/Index/Mocks/MockBTree.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using MarcelloDB.Index; 4 | using MarcelloDB.Index.BTree; 5 | 6 | namespace MarcelloDB.Test 7 | { 8 | internal class MockBTree : IBTree 9 | { 10 | public string LastAction { get; set; } 11 | public List Searched { get; set; } 12 | public Dictionary Inserted { get; set; } 13 | public List Deleted { get; set; } 14 | 15 | public MockBTree() 16 | { 17 | Searched = new List(); 18 | Inserted = new Dictionary(); 19 | Deleted = new List(); 20 | } 21 | 22 | 23 | #region IBTree implementation 24 | public Entry Search(TK key) 25 | { 26 | Searched.Add(key); 27 | if(Inserted.ContainsKey(key) && !Deleted.Contains(key)){ 28 | return new Entry{Key=key, Pointer=Inserted[key]}; 29 | } 30 | return null; 31 | } 32 | 33 | public void Insert(TK newKey, Int64 newPointer) 34 | { 35 | Inserted[newKey] = newPointer; 36 | LastAction = "Insert"; 37 | } 38 | 39 | public void Delete(TK keyToDelete) 40 | { 41 | Inserted.Remove(keyToDelete); 42 | Deleted.Add(keyToDelete); 43 | LastAction = "Delete"; 44 | } 45 | #endregion 46 | } 47 | } 48 | 49 | -------------------------------------------------------------------------------- /MarcelloDB.Test/Serialization/CollectionFileRootSerializerTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using MarcelloDB.Serialization; 4 | using System.Linq; 5 | 6 | namespace MarcelloDB.Test.Serialization 7 | { 8 | [TestFixture] 9 | public class CollectionFileRootSerializerTest 10 | { 11 | CollectionFileRootSerializer _serializer; 12 | 13 | [SetUp] 14 | public void Initialize() 15 | { 16 | _serializer = new CollectionFileRootSerializer(); 17 | } 18 | 19 | [Test] 20 | public void SerializesCollectionFileRoot() 21 | { 22 | var root = new CollectionFileRoot(); 23 | root.Head = 456; 24 | root.NamedRecordIndexAddress = 7; 25 | root.FormatVersion = 8; 26 | 27 | var deserialized = _serializer.Deserialize(_serializer.Serialize(root)); 28 | 29 | Assert.AreEqual(456, deserialized.Head); 30 | Assert.AreEqual(8, deserialized.FormatVersion); 31 | } 32 | 33 | [Test] 34 | public void DeserializedShouldNotBeDirty() 35 | { 36 | var root = new CollectionFileRoot(); 37 | root.Head = 456; 38 | root.NamedRecordIndexAddress = 7; 39 | root.FormatVersion = 8; 40 | 41 | var deserialized = _serializer.Deserialize(_serializer.Serialize(root)); 42 | 43 | Assert.IsFalse(deserialized.IsDirty); 44 | } 45 | } 46 | } 47 | 48 | -------------------------------------------------------------------------------- /MarcelloDB/Helpers/DataHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MarcelloDB.Helpers 4 | { 5 | internal class DataHelper 6 | { 7 | internal static void CopyData( 8 | Int64 sourceAddress, 9 | byte[] sourceData, 10 | Int64 targetAddress, 11 | byte[] targetData) 12 | { 13 | Int64 lengthToCopy = sourceData.Length; 14 | Int64 sourceIndex = 0; 15 | Int64 targetIndex = 0; 16 | 17 | if (sourceAddress < targetAddress) 18 | { 19 | sourceIndex = (targetAddress - sourceAddress); 20 | lengthToCopy = sourceData.Length - sourceIndex; 21 | } 22 | 23 | if (sourceAddress > targetAddress) 24 | { 25 | targetIndex = (sourceAddress - targetAddress); 26 | lengthToCopy = targetData.Length - targetIndex; 27 | } 28 | 29 | //max length to copy to not overrun the target array 30 | lengthToCopy = Math.Min(lengthToCopy, targetData.Length - targetIndex); 31 | //max length to copy to not overrrun the source array 32 | lengthToCopy = Math.Min(lengthToCopy, sourceData.Length - sourceIndex); 33 | 34 | if (lengthToCopy > 0) 35 | { 36 | Array.Copy(sourceData, (int)sourceIndex, targetData, (int)targetIndex, (int)lengthToCopy); 37 | } 38 | 39 | } 40 | } 41 | } 42 | 43 | -------------------------------------------------------------------------------- /MarcelloDB.Test/Regression/Issue74_ItemWithSameKeyAllreadyAdded.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using MarcelloDB.Collections; 6 | using MarcelloDB.Index; 7 | using NUnit.Framework; 8 | 9 | namespace MarcelloDB.Test.Regression 10 | { 11 | public class Product 12 | { 13 | public Guid Id { get; set; } 14 | public bool IsGood { get; set; } 15 | } 16 | 17 | [TestFixture] 18 | public class Issue74_ItemWithSameKeyAllreadyAdded 19 | { 20 | Session _session; 21 | CollectionFile _collectionFile; 22 | TestPlatform _platform; 23 | 24 | [SetUp] 25 | public void Setup () 26 | { 27 | _platform = new TestPlatform (); 28 | _session = new Session (_platform, "/"); 29 | _collectionFile = _session ["articles"]; 30 | } 31 | 32 | [Test] 33 | public void ParallelCreateCollectionFile() 34 | { 35 | var tasks = new List (); 36 | tasks.Add(Task.Run (() => { 37 | var collection = _collectionFile.Collection ("products", p => p.Id); 38 | })); 39 | tasks.Add (Task.Run (() => { 40 | var collection = _collectionFile.Collection ("products", p => p.Id); 41 | })); 42 | 43 | 44 | Task.WaitAll (tasks.ToArray ()); 45 | 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /MarcelloDB.Test/Records/EmptyRecordIndexKeyTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using MarcelloDB.Serialization; 4 | using MarcelloDB.Records; 5 | 6 | namespace MarcelloDB.Test.Records 7 | { 8 | [TestFixture] 9 | public class EmptyRecordIndexKeyTest 10 | { 11 | [SetUp] 12 | public void Initialize() 13 | { 14 | } 15 | 16 | [Test] 17 | public void Can_Be_Serialized() 18 | { 19 | var key = new EmptyRecordIndexKey{A = 10, S = 20 }; 20 | var serializer = new BsonSerializer(); 21 | var deserialized = serializer.Deserialize( 22 | serializer.Serialize(key) 23 | ); 24 | Assert.AreEqual(key.A, deserialized.A); 25 | Assert.AreEqual(key.S, deserialized.S); 26 | } 27 | 28 | [Test] 29 | public void Compares_On_Size_First() 30 | { 31 | var key1 = new EmptyRecordIndexKey{S = 1, A = 2 }; 32 | var key2 = new EmptyRecordIndexKey{S = 2, A = 1 }; 33 | Assert.IsTrue(key1.CompareTo(key2) < 0); 34 | } 35 | 36 | [Test] 37 | public void Compares_On_Address_When_Size_Is_Equal() 38 | { 39 | var key1 = new EmptyRecordIndexKey{S = 1, A = 1 }; 40 | var key2 = new EmptyRecordIndexKey{S = 1, A = 2 }; 41 | Assert.IsTrue(key1.CompareTo(key2) < 0); 42 | } 43 | } 44 | } 45 | 46 | -------------------------------------------------------------------------------- /UniversalTest/UniversalTest.Windows/Package.appxmanifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | UniversalTest.Windows 10 | markm_000 11 | Assets\StoreLogo.png 12 | 13 | 14 | 15 | 6.3.0 16 | 6.3.0 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 27 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /docs/roadmap.md: -------------------------------------------------------------------------------- 1 | #Roadmap 2 | 0.1.0 3 | - 4 | - ~~Persisting objects~~ 5 | - ~~Deleting objects~~ 6 | - ~~enumerating objects~~ 7 | - ~~reusing storage of deleted objects~~ 8 | - ~~cross-collection transaction support~~ 9 | - ~~Indexing~~ 10 | - ~~iOS/Android (Xamarin) support~~ 11 | - ~~Polymorphic collections with Polymorphic children~~ 12 | - ~~Windows 8.1 Support~~ 13 | - ~~Windows Phone 8.1 Support~~ 14 | 15 | 0.2.0 16 | - 17 | - ~~Indexing properties~~ 18 | - ~~Indexing user-defined values~~ 19 | - ~~Iterating indexes~~ 20 | - ~~Iterating a range from an index~~ 21 | 22 | 0.3.0 23 | - 24 | - ~~Iterating indexes in descending order~~ 25 | - ~~Iterating index keys only~~ 26 | 27 | 0.4.0 (Performance optimizations) 28 | - 29 | - ~~Use custom btree node serialization~~ 30 | - ~~Use Generic IDs~~ 31 | - ~~Remove unnecessary serializations~~ 32 | 33 | 0.5.0 34 | - 35 | - ~~Compound indexes~~ 36 | - ~~Indexing list of values~~ 37 | - ~~Contains/ContainsAll query~~ 38 | 39 | 0.6.0 40 | - 41 | - ~~Unique indexes~~ 42 | - ~~Conditional Indexes (to exclude nulls f.i.)~~ 43 | 44 | 1.0.0 45 | - 46 | - ~~Stable file format, to be supported in all future 1.x versions~~ 47 | 48 | 1.1.0 49 | - 50 | - Named objects (singleton objects per collection) 51 | - ClearAllCollections in collection files 52 | 53 | 1.2.0 54 | - 55 | - Data migration mechanism 56 | 57 | 1.3.0 58 | - 59 | - Updates while enumerating 60 | - Rebuilding indexes 61 | 62 | Read [long term plans](longterm.md) to see what may be comming later. 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /MarcelloDB.Test/Index/NodeTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using MarcelloDB.Index; 4 | 5 | 6 | namespace MarcelloDB.Test.Index 7 | { 8 | [TestFixture] 9 | public class NodeTest 10 | { 11 | Node _node; 12 | 13 | [SetUp] 14 | public void Initialize() 15 | { 16 | _node = new Node(2); 17 | } 18 | 19 | [Test] 20 | public void New_Node_Is_Not_Dirty() 21 | { 22 | Assert.IsFalse(_node.Dirty); 23 | } 24 | 25 | [Test] 26 | public void Node_With_Added_Address_Is_Dirty() 27 | { 28 | _node.ChildrenAddresses.Add(123); 29 | Assert.IsTrue(_node.Dirty); 30 | } 31 | 32 | [Test] 33 | public void Clear_Changes_Clears_Address_Changes() 34 | { 35 | _node.ChildrenAddresses.Add(123); 36 | _node.ClearChanges(); 37 | Assert.IsFalse(_node.Dirty); 38 | } 39 | 40 | [Test] 41 | public void Node_With_Added_Entries_Is_Dirty() 42 | { 43 | _node.EntryList.Add(new Entry{Key=123, Pointer=456}); 44 | Assert.IsTrue(_node.Dirty);Assert.IsTrue(_node.Dirty); 45 | } 46 | 47 | [Test] 48 | public void Clear_Changes_Clears_EntryList_Changes() 49 | { 50 | _node.EntryList.Add(new Entry{Key=123, Pointer=456}); 51 | _node.ClearChanges(); 52 | Assert.IsFalse(_node.Dirty); 53 | } 54 | } 55 | } 56 | 57 | -------------------------------------------------------------------------------- /MarcelloDB/Serialization/BtreeNodeSerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using MarcelloDB.Index; 3 | using System.Collections.Generic; 4 | 5 | namespace MarcelloDB.Serialization 6 | { 7 | internal class BTreeNodeBsonSerializer : IObjectSerializer> 8 | { 9 | BsonSerializer> DataSerializer { get; set; } 10 | 11 | public class BTreeNodeData 12 | { 13 | public List> Entries { get; set; } 14 | public List ChildrenAddresses { get; set; } 15 | } 16 | 17 | #region IObjectSerializer implementation 18 | 19 | public BTreeNodeBsonSerializer() 20 | { 21 | this.DataSerializer = new BsonSerializer>(); 22 | } 23 | 24 | public byte[] Serialize(Node node) 25 | { 26 | var data = new BTreeNodeData(); 27 | data.Entries = node.EntryList.Entries; 28 | data.ChildrenAddresses = node.ChildrenAddresses.Addresses; 29 | return this.DataSerializer.Serialize(data); 30 | } 31 | 32 | public Node Deserialize(byte[] bytes) 33 | { 34 | var data = this.DataSerializer.Deserialize(bytes); 35 | var node = new Node(RecordIndex.BTREE_DEGREE); 36 | node.EntryList.SetEntries(data.Entries); 37 | node.ChildrenAddresses.SetAddresses(data.ChildrenAddresses); 38 | 39 | return node; 40 | } 41 | 42 | #endregion 43 | } 44 | } 45 | 46 | -------------------------------------------------------------------------------- /MarcelloDB.BenchmarkTool/DataClasses/Person.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using MarcelloDB.Index; 4 | using MarcelloDB.Collections; 5 | 6 | namespace MarcelloDB.BenchmarkTool.DataClasses 7 | { 8 | public class PersonIndexes : IndexDefinition 9 | { 10 | public IndexedValue FirstName { get; set; } 11 | public IndexedValue LastName { get; set; } 12 | public IndexedValue FullName 13 | { 14 | get { 15 | return base.IndexedValue(p => string.Format("{0} {1}", p.FirstName, p.LastName)); 16 | } 17 | } 18 | } 19 | 20 | public class Person: Base 21 | { 22 | static Random _rand = new Random(); 23 | 24 | public int ID { get; set; } 25 | public string FirstName { get; set;} 26 | public string LastName { get; set; } 27 | public List
Addresses { get; set; } 28 | 29 | public static Person BuildRandom() 30 | { 31 | var person = new Person() 32 | { 33 | FirstName = "First " + _rand.Next().ToString(), 34 | LastName = "Last " + _rand.Next().ToString(), 35 | Addresses = new List
() 36 | }; 37 | 38 | var addressCount = _rand.Next() % 4; 39 | for (int i = 0; i < addressCount; i++) 40 | { 41 | person.Addresses.Add(Address.BuildRandom()); 42 | } 43 | 44 | return person; 45 | } 46 | } 47 | } 48 | 49 | -------------------------------------------------------------------------------- /MarcelloDB/Serialization/IndexMetaRecordSerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using MarcelloDB.Records; 3 | 4 | namespace MarcelloDB.Serialization 5 | { 6 | internal class IndexMetaRecordSerializer : IObjectSerializer 7 | { 8 | #region IObjectSerializer implementation 9 | 10 | public byte[] Serialize(IndexMetaRecord record) 11 | { 12 | var bytes = new byte[ 5 * sizeof(Int64)]; 13 | var writer = new BufferWriter(bytes); 14 | writer.WriteInt64(record.RootNodeAddress); 15 | writer.WriteInt64(record.NumberOfEntries); 16 | writer.WriteInt64(record.NumberOfNodes); 17 | writer.WriteInt64(record.TotalAllocatedSize); 18 | writer.WriteInt64(record.TotalAllocatedDataSize); 19 | return writer.GetTrimmedBuffer(); 20 | } 21 | 22 | public IndexMetaRecord Deserialize(byte[] bytes) 23 | { 24 | var reader = new BufferReader(bytes); 25 | var deserializedRecord = new IndexMetaRecord(); 26 | deserializedRecord.RootNodeAddress = reader.ReadInt64(); 27 | deserializedRecord.NumberOfEntries = reader.ReadInt64(); 28 | deserializedRecord.NumberOfNodes = reader.ReadInt64(); 29 | deserializedRecord.TotalAllocatedSize = reader.ReadInt64(); 30 | deserializedRecord.TotalAllocatedDataSize = reader.ReadInt64(); 31 | 32 | deserializedRecord.Clean(); 33 | return deserializedRecord; 34 | } 35 | 36 | #endregion 37 | 38 | } 39 | } 40 | 41 | -------------------------------------------------------------------------------- /MarcelloDB/Collections/Scopes/GreaterThan.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections; 4 | using MarcelloDB.Records; 5 | using MarcelloDB.Index.BTree; 6 | 7 | namespace MarcelloDB.Collections.Scopes 8 | { 9 | public class GreaterThan : BaseScope 10 | { 11 | BaseIndexedValue IndexedValue { get; set; } 12 | 13 | TAttribute Value { get; set; } 14 | 15 | bool OrEqual { get; set; } 16 | 17 | internal GreaterThan(BaseIndexedValue indexedValue, TAttribute value, bool orEqual) 18 | { 19 | this.IndexedValue = indexedValue; 20 | this.Value = value; 21 | this.OrEqual = orEqual; 22 | } 23 | 24 | internal override CollectionEnumerator> BuildEnumerator(bool descending) 25 | { 26 | var startKey = new ValueWithAddressIndexKey{ V = (TAttribute)this.Value }; 27 | var range = new BTreeWalkerRange>(); 28 | if (!descending) 29 | { 30 | range.SetStartAt(startKey); 31 | range.IncludeStartAt = this.OrEqual; 32 | } 33 | else 34 | { 35 | range.SetEndAt(startKey); 36 | range.IncludeEndAt = this.OrEqual; 37 | } 38 | 39 | return this.IndexedValue.BuildEnumerator(new BTreeWalkerRange>[]{range}, descending); 40 | } 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /MarcelloDB/Serialization/CollectionFileRootSerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | namespace MarcelloDB.Serialization 5 | { 6 | internal class CollectionFileRootSerializer : IObjectSerializer 7 | { 8 | const int SERIALIZATION_VERSION = 1; 9 | 10 | public CollectionFileRootSerializer() 11 | { 12 | } 13 | 14 | #region IObjectSerializer implementation 15 | 16 | public byte[] Serialize(CollectionFileRoot root) 17 | { 18 | var expectedSize = sizeof(byte) 19 | + sizeof(Int32) 20 | + sizeof(Int64) 21 | + sizeof(Int64); 22 | var writer = new BufferWriter(new byte[expectedSize]); 23 | writer.WriteByte(SERIALIZATION_VERSION); 24 | writer.WriteInt32(root.FormatVersion); 25 | writer.WriteInt64(root.Head); 26 | writer.WriteInt64(root.NamedRecordIndexAddress); 27 | 28 | return writer.GetTrimmedBuffer(); 29 | } 30 | 31 | public CollectionFileRoot Deserialize(byte[] bytes) 32 | { 33 | var reader = new BufferReader(bytes); 34 | var root = new CollectionFileRoot(); 35 | reader.ReadByte(); //read SERIALIZATION_VERSION 36 | root.FormatVersion = reader.ReadInt32(); 37 | root.Head = reader.ReadInt64(); 38 | root.NamedRecordIndexAddress = reader.ReadInt64(); 39 | 40 | root.Clean(); 41 | return root; 42 | } 43 | 44 | #endregion 45 | } 46 | } 47 | 48 | -------------------------------------------------------------------------------- /MarcelloDB/Collections/Scopes/SmallerThan.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using MarcelloDB.Records; 4 | using MarcelloDB.Index.BTree; 5 | using System.Collections; 6 | 7 | namespace MarcelloDB.Collections.Scopes 8 | { 9 | public class SmallerThan : BaseScope 10 | { 11 | BaseIndexedValue IndexedValue { get; set; } 12 | 13 | TAttribute Value { get; set; } 14 | 15 | bool OrEqual { get; set; } 16 | 17 | internal SmallerThan(BaseIndexedValue indexedValue, TAttribute value, bool orEqual) 18 | { 19 | this.IndexedValue = indexedValue; 20 | this.Value = value; 21 | this.OrEqual = orEqual; 22 | } 23 | 24 | internal override CollectionEnumerator> BuildEnumerator(bool descending) 25 | { 26 | var startKey = new ValueWithAddressIndexKey{ 27 | V = this.Value 28 | }; 29 | var range = new BTreeWalkerRange>(); 30 | 31 | if (!descending) 32 | { 33 | range.SetEndAt(startKey); 34 | range.IncludeEndAt = this.OrEqual; 35 | } 36 | else 37 | { 38 | range.SetStartAt(startKey); 39 | range.IncludeStartAt = this.OrEqual; 40 | } 41 | 42 | return this.IndexedValue 43 | .BuildEnumerator(new BTreeWalkerRange>[]{range}, descending); 44 | } 45 | } 46 | } 47 | 48 | -------------------------------------------------------------------------------- /docs/template/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | currentMenu: home 3 | --- 4 | # Couscous Dark template 5 | 6 | ![](screenshot.png) 7 | 8 | ## Usage 9 | 10 | To use the template, set it up in your `couscous.yml` configuration file: 11 | 12 | ```yaml 13 | template: 14 | url: https://github.com/CouscousPHP/Template-Dark 15 | ``` 16 | 17 | ## Configuration 18 | 19 | Here are all the variables you can set in your `couscous.yml`: 20 | 21 | ```yaml 22 | # Base URL of the published website 23 | baseUrl: http://username.github.io/project 24 | 25 | # Used to link to the GitHub project 26 | github: 27 | user: myself 28 | repo: my-project 29 | 30 | title: My project 31 | subTitle: This is a great project. 32 | 33 | # The left menu bar 34 | menu: 35 | sections: 36 | main: 37 | name: Main documentation 38 | items: 39 | home: 40 | text: Home page 41 | # You can use relative urls 42 | relativeUrl: doc/faq.html 43 | foo: 44 | text: Another link 45 | # Or absolute urls 46 | absoluteUrl: https://example.com 47 | other: 48 | name: Other topics 49 | items: 50 | ``` 51 | 52 | Note that the menu items can also contain HTML: 53 | 54 | ```yaml 55 | home: 56 | text: " Home page" 57 | relativeUrl: doc/faq.html 58 | ``` 59 | 60 | ## Menu 61 | 62 | To set the current menu item (i.e. highlighted menu item), set the `currentMenu` 63 | key in the Markdown files: 64 | 65 | ```markdown 66 | --- 67 | currentMenu: home 68 | --- 69 | 70 | # Welcome 71 | ``` 72 | -------------------------------------------------------------------------------- /docs/template/css/highlight.tomorrow-night.css: -------------------------------------------------------------------------------- 1 | /* Tomorrow Night Theme */ 2 | /* http://jmblog.github.com/color-themes-for-google-code-highlightjs */ 3 | /* Original theme - https://github.com/chriskempson/tomorrow-theme */ 4 | /* http://jmblog.github.com/color-themes-for-google-code-highlightjs */ 5 | .tomorrow-comment, pre .comment, pre .title { 6 | color: #969896; 7 | } 8 | 9 | .tomorrow-red, pre .variable, pre .attribute, pre .tag, pre .regexp, pre .ruby .constant, pre .xml .tag .title, pre .xml .pi, pre .xml .doctype, pre .html .doctype, pre .css .id, pre .css .class, pre .css .pseudo { 10 | color: #cc6666; 11 | } 12 | 13 | .tomorrow-orange, pre .number, pre .preprocessor, pre .built_in, pre .literal, pre .params, pre .constant { 14 | color: #de935f; 15 | } 16 | 17 | .tomorrow-yellow, pre .class, pre .ruby .class .title, pre .css .rules .attribute { 18 | color: #f0c674; 19 | } 20 | 21 | .tomorrow-green, pre .string, pre .value, pre .inheritance, pre .header, pre .ruby .symbol, pre .xml .cdata { 22 | color: #b5bd68; 23 | } 24 | 25 | .tomorrow-aqua, pre .css .hexcolor { 26 | color: #8abeb7; 27 | } 28 | 29 | .tomorrow-blue, pre .function, pre .python .decorator, pre .python .title, pre .ruby .function .title, pre .ruby .title .keyword, pre .perl .sub, pre .javascript .title, pre .coffeescript .title { 30 | color: #81a2be; 31 | } 32 | 33 | .tomorrow-purple, pre .keyword, pre .javascript .function { 34 | color: #b294bb; 35 | } 36 | 37 | pre code { 38 | display: block; 39 | background: #1d1f21; 40 | color: #c5c8c6; 41 | font-family: Menlo, Monaco, Consolas, monospace; 42 | line-height: 1.5; 43 | border: 1px solid #ccc; 44 | padding: 10px; 45 | } 46 | -------------------------------------------------------------------------------- /MarcelloDB.Test/Serialization/BsonSerializerTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using MarcelloDB.Serialization; 4 | using MarcelloDB.Test.Classes; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | 8 | namespace MarcelloDB.Test.Serialization 9 | { 10 | [TestFixture] 11 | public class BsonSerializerTest 12 | { 13 | [Test] 14 | public void Serializes_And_Deserializes_Simple_Object() 15 | { 16 | var serializer = new BsonSerializer
(); 17 | var article = Article.SpinalTapDvd; 18 | var deserializedArticle = (Article)serializer.Deserialize( 19 | serializer.Serialize(article) 20 | ); 21 | Assert.AreEqual(article.ID, deserializedArticle.ID); 22 | Assert.AreEqual(article.Name, deserializedArticle.Name); 23 | } 24 | 25 | [Test] 26 | public void Serializes_Subclasses_In_list() 27 | { 28 | var serializer = new BsonSerializer>(); 29 | var list = new List
{ Article.BarbieDoll, Food.Bread }; 30 | var deserializedList = (List
)serializer.Deserialize( 31 | serializer.Serialize(list) 32 | ); 33 | Assert.AreEqual(list[0].ID, deserializedList[0].ID); 34 | Assert.AreEqual(list[0].Name, deserializedList[0].Name); 35 | Assert.AreEqual(list[1].ID, deserializedList[1].ID); 36 | Assert.AreEqual(list[1].Name, deserializedList[1].Name); 37 | Assert.AreEqual(((Food)list[1]).Expires.ToString(), ((Food)deserializedList[1]).Expires.ToString()); 38 | } 39 | } 40 | } 41 | 42 | -------------------------------------------------------------------------------- /MarcelloDB.uwp/Properties/MarcelloDB.uwp.rd.xml: -------------------------------------------------------------------------------- 1 | 2 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /MarcelloDB/Records/ValueWithAddressIndexKey.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using MarcelloDB.Index; 3 | 4 | namespace MarcelloDB.Records 5 | { 6 | internal class ValueWithAddressIndexKey : IComparable, IEquatable> 7 | { 8 | public Int64 A { get; set; } //Address, abbreviated to save storage 9 | public TValue V { get; set; } //Size 10 | 11 | #region IComparable implementation 12 | 13 | public int CompareTo(object obj) 14 | { 15 | var other = (ValueWithAddressIndexKey)obj; 16 | var valueCompared = CompareValues(other); 17 | if(valueCompared == 0) 18 | { 19 | if (A > 0 && other.A > 0) //no address is any of them considered equal 20 | { 21 | return A.CompareTo(other.A); 22 | } 23 | } 24 | return valueCompared; 25 | } 26 | 27 | #endregion 28 | 29 | #region IEquatable implementation 30 | 31 | public bool Equals(ValueWithAddressIndexKey other) 32 | { 33 | return this.CompareTo(other) == 0; 34 | } 35 | 36 | #endregion 37 | 38 | int CompareValues(ValueWithAddressIndexKey other) 39 | { 40 | if (this.V == null) 41 | { 42 | if (other.V == null) 43 | { 44 | return 0; 45 | } 46 | return -1; 47 | } 48 | else if (other.V == null) 49 | { 50 | return 1; 51 | } 52 | 53 | return new ObjectComparer().Compare(this.V, other.V); 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /MarcelloDB.BenchmarkTool/Benchmarks/Base.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Collections.Generic; 4 | using MarcelloDB.BenchmarkTool.DataClasses; 5 | using System.IO; 6 | 7 | namespace MarcelloDB.BenchmarkTool.Benchmarks 8 | { 9 | public class Base 10 | { 11 | protected MarcelloDB.Collections.Collection Collection {get; set;} 12 | 13 | public Base() 14 | { 15 | 16 | } 17 | 18 | protected virtual void OnSetup(){} 19 | 20 | protected virtual void OnRun() 21 | { 22 | } 23 | 24 | public TimeSpan Run() 25 | { 26 | Stopwatch w; 27 | var dataPath = Path.Combine(Environment.CurrentDirectory, "data"); 28 | EnsureFolder(dataPath); 29 | var platform = new MarcelloDB.netfx.Platform(); 30 | using (var session = new Session(platform, dataPath)) 31 | { 32 | this.Collection = session["data"].Collection("persons", p => p.ID); 33 | OnSetup(); 34 | 35 | w = Stopwatch.StartNew(); 36 | OnRun(); 37 | } 38 | 39 | w.Stop(); 40 | return w.Elapsed; 41 | } 42 | 43 | private void EnsureFolder(string path) 44 | { 45 | if(System.IO.Directory.Exists(path)) 46 | { 47 | foreach( var file in System.IO.Directory.EnumerateFiles(path)) 48 | { 49 | System.IO.File.Delete(file); 50 | } 51 | } 52 | else 53 | { 54 | System.IO.Directory.CreateDirectory(path); 55 | } 56 | } 57 | } 58 | } 59 | 60 | -------------------------------------------------------------------------------- /MarcelloDB.Test/Index/Mocks/MockBTreeDataProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using MarcelloDB.Index; 3 | using System.Collections.Generic; 4 | using MarcelloDB.Index.BTree; 5 | 6 | namespace MarcelloDB.Test 7 | { 8 | internal class MockBTreeDataProvider : IBTreeDataProvider 9 | { 10 | public bool WasFlushed { get; set; } 11 | 12 | Int64 _lastAddress = 1; 13 | Dictionary> _nodes = new Dictionary>(); 14 | 15 | Node _rootNode; 16 | 17 | public Int64 RootNodeAddress { get; set; } 18 | 19 | Node IBTreeDataProvider.GetRootNode(int degree) 20 | { 21 | if (_rootNode == null) 22 | { 23 | _rootNode = new Node(degree); 24 | _rootNode.Address = 0; 25 | AddNode(_rootNode); 26 | } 27 | return _rootNode; 28 | } 29 | 30 | void IBTreeDataProvider.SetRootNode(Node rootNode) 31 | { 32 | _rootNode = rootNode; 33 | this.RootNodeAddress = rootNode.Address; 34 | } 35 | 36 | Node IBTreeDataProvider.GetNode(Int64 address) 37 | { 38 | return _nodes[address]; 39 | } 40 | 41 | Node IBTreeDataProvider.CreateNode(int degree, bool allowRecordReuse = true) 42 | { 43 | var node = new Node(degree); 44 | node.Address = _lastAddress++; 45 | AddNode(node); 46 | return node; 47 | } 48 | 49 | void AddNode(Node node) 50 | { 51 | _nodes.Add(node.Address, node); 52 | } 53 | 54 | public void Flush() 55 | { 56 | this.WasFlushed = true; 57 | } 58 | } 59 | } 60 | 61 | -------------------------------------------------------------------------------- /MarcelloDB/Collections/IndexedValueTypes/IndexedIDValue.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using MarcelloDB.Collections; 3 | using MarcelloDB.Records; 4 | using MarcelloDB.Index; 5 | using System.Collections.Generic; 6 | 7 | namespace MarcelloDB.Collections 8 | { 9 | internal class IndexedIDValue : IndexedValue 10 | { 11 | internal IndexedIDValue():base(null, null) 12 | { 13 | this.PropertyName = "ID"; 14 | } 15 | 16 | internal Func IDValueFunction { get; set; } 17 | 18 | internal override IEnumerable GetKeys(object o, long address) 19 | { 20 | return new object[]{IDValueFunction((TObj)o)}; 21 | } 22 | 23 | internal override void RegisterKey(object key, 24 | Int64 address, 25 | Session session, 26 | RecordManager recordManager, 27 | string indexName) 28 | { 29 | var index = new RecordIndex( 30 | this.Session, 31 | recordManager, 32 | indexName, 33 | this.Session.SerializerResolver.SerializerFor>() 34 | ); 35 | 36 | index.Register((TID)key, address); 37 | } 38 | 39 | internal override void UnRegisterKey(object key, 40 | Int64 address, 41 | Session session, 42 | RecordManager recordManager, 43 | string indexName) 44 | { 45 | var index = new RecordIndex( 46 | this.Session, 47 | recordManager, 48 | indexName, 49 | this.Session.SerializerResolver.SerializerFor>() 50 | ); 51 | 52 | index.UnRegister((TID)key); 53 | } 54 | } 55 | } 56 | 57 | -------------------------------------------------------------------------------- /MarcelloDB/Records/Record.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | 4 | namespace MarcelloDB.Records 5 | { 6 | internal class Record 7 | { 8 | internal RecordHeader Header { get; set;} 9 | 10 | private byte[] _data; 11 | internal byte[] Data 12 | { 13 | get{return _data; } 14 | set 15 | { 16 | if (value.Length > this.Header.AllocatedDataSize) 17 | { 18 | throw new Exception("PANIC: Data cannot exceed AllocatedDataSize"); 19 | } 20 | 21 | this.Header.DataSize = value.Length; 22 | _data = value; 23 | } 24 | } 25 | 26 | 27 | public Record() 28 | { 29 | Header = RecordHeader.New(); 30 | } 31 | 32 | internal Int32 ByteSize 33 | { 34 | get 35 | { 36 | return RecordHeader.ByteSize + Header.AllocatedDataSize; 37 | } 38 | } 39 | 40 | internal virtual byte[] AsBytes() 41 | { 42 | var bytes = new byte[RecordHeader.ByteSize + Data.Length]; 43 | 44 | Header.AsBytes().CopyTo(bytes, 0); 45 | Data.CopyTo(bytes, RecordHeader.ByteSize); 46 | 47 | return bytes; 48 | } 49 | 50 | internal static Record FromBytes(Int64 address, byte[] bytes) 51 | { 52 | var header = RecordHeader.FromBytes(address, bytes); 53 | var data = new byte[header.DataSize]; 54 | 55 | Array.Copy(bytes, RecordHeader.ByteSize, data, 0, header.DataSize); 56 | 57 | return new Record() 58 | { 59 | Header = header, 60 | Data = data 61 | }; 62 | } 63 | } 64 | } 65 | 66 | -------------------------------------------------------------------------------- /docs/template/css/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-size: 19px; 3 | } 4 | 5 | main { 6 | margin-top: 90px; 7 | } 8 | 9 | section { 10 | margin-bottom: 50px; 11 | } 12 | 13 | h1, h2, h3, h4 { 14 | color: #df691a; 15 | } 16 | h3 { 17 | font-size: 23px; 18 | } 19 | 20 | li { 21 | margin-bottom: 3px; 22 | } 23 | 24 | img { 25 | max-width: 100%; 26 | } 27 | 28 | header.navbar { 29 | opacity: 0.9; 30 | } 31 | .navbar .navbar-brand { 32 | font-size: 28px; 33 | height: auto; 34 | line-height: 50px; 35 | margin-left: 20px; 36 | color: #df691a; 37 | } 38 | .navbar a.navbar-brand:hover { 39 | color: #df691a; 40 | } 41 | .navbar .navbar-brand small { 42 | font-size: 18px; 43 | font-weight: 300; 44 | margin-left: 10px; 45 | color: white; 46 | } 47 | 48 | @media (min-width: 768px) { 49 | #sidebar { 50 | position:absolute; 51 | } 52 | } 53 | @media (max-width: 960px) { 54 | body { 55 | font-size: 17px; 56 | } 57 | pre { 58 | font-size: 12px; 59 | } 60 | } 61 | 62 | .page-header { 63 | margin-top: 0; 64 | } 65 | 66 | #sidebar .github-star { 67 | margin-top: 20px; 68 | margin-left: 50px; 69 | } 70 | 71 | #sidebar .text-muted { 72 | color: #859AAF; 73 | } 74 | 75 | pre { 76 | padding: 0; 77 | border-color: #3D5166; 78 | background-color: #1D2B3A; 79 | border-radius: 4px; 80 | margin: 15px; 81 | } 82 | pre code { 83 | border: none; 84 | background-color: #1D2B3A; 85 | } 86 | 87 | code { 88 | font-size: 85%; 89 | padding: 4px 4px 1px; 90 | margin: 0 4px; 91 | border-radius: 3px; 92 | color: #c5c8c6; 93 | border: solid 1px #3D5166; 94 | background-color: #1D2B3A; 95 | white-space: pre-wrap; 96 | white-space: -moz-pre-wrap; 97 | word-wrap: break-word; 98 | } 99 | -------------------------------------------------------------------------------- /UniversalTest/UniversalTest.WindowsPhone/MainPage.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Runtime.InteropServices.WindowsRuntime; 6 | using Windows.Foundation; 7 | using Windows.Foundation.Collections; 8 | using Windows.UI.Xaml; 9 | using Windows.UI.Xaml.Controls; 10 | using Windows.UI.Xaml.Controls.Primitives; 11 | using Windows.UI.Xaml.Data; 12 | using Windows.UI.Xaml.Input; 13 | using Windows.UI.Xaml.Media; 14 | using Windows.UI.Xaml.Navigation; 15 | 16 | // The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238 17 | 18 | namespace UniversalTest 19 | { 20 | /// 21 | /// An empty page that can be used on its own or navigated to within a Frame. 22 | /// 23 | public sealed partial class MainPage : Page 24 | { 25 | public MainPage() 26 | { 27 | this.InitializeComponent(); 28 | 29 | this.NavigationCacheMode = NavigationCacheMode.Required; 30 | } 31 | 32 | /// 33 | /// Invoked when this page is about to be displayed in a Frame. 34 | /// 35 | /// Event data that describes how this page was reached. 36 | /// This parameter is typically used to configure the page. 37 | protected override void OnNavigatedTo(NavigationEventArgs e) 38 | { 39 | // TODO: Prepare page for display here. 40 | 41 | // TODO: If your application contains multiple pages, ensure that you are 42 | // handling the hardware Back button by registering for the 43 | // Windows.Phone.UI.Input.HardwareButtons.BackPressed event. 44 | // If you are using the NavigationHelper provided by some templates, 45 | // this event is handled for you. 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /MarcelloDB.Test/AllocationStrategies/AllocationStrategyResolverTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using MarcelloDB.Records; 4 | using MarcelloDB.AllocationStrategies; 5 | using MarcelloDB.Index; 6 | 7 | namespace MarcelloDB.Test.AllocationStrategies 8 | { 9 | [TestFixture] 10 | public class AllocationStrategyResolverTest 11 | { 12 | 13 | AllocationStrategyResolver resolver; 14 | 15 | [SetUp] 16 | public void Initialize() 17 | { 18 | resolver = new AllocationStrategyResolver(); 19 | } 20 | 21 | [Test] 22 | public void StrategyFor_EmptyRecordIndexNode_ReturnsFixedSizeStrategy() 23 | { 24 | var strategy = resolver.StrategyFor( 25 | new Node(2)); 26 | Assert.AreEqual(typeof(ExactSizeAllocationStrategy), strategy.GetType()); 27 | } 28 | 29 | [Test] 30 | public void StrategyFor_EmptyRecordIndexNode_As_Object_ReturnsFixedSizeStrategy() 31 | { 32 | var strategy = resolver.StrategyFor( 33 | (object)new Node(2)); 34 | Assert.AreEqual(typeof(ExactSizeAllocationStrategy), strategy.GetType()); 35 | } 36 | 37 | [Test] 38 | public void StrategyFor_BTreeNodes_Returns_PredictiveBTReeNodeAllocationStrategy() 39 | { 40 | var strategy = resolver.StrategyFor( 41 | new Node(2)); 42 | Assert.AreEqual(typeof(PredictiveBTreeNodeAllocationStrategy), strategy.GetType()); 43 | } 44 | 45 | [Test] 46 | public void Strategy_For_Object_Returns_DoubleSizeStrategy() 47 | { 48 | var strategy = resolver.StrategyFor(new object()); 49 | Assert.AreEqual(typeof(DoubleSizeAllocationStrategy), strategy.GetType()); 50 | } 51 | } 52 | } 53 | 54 | -------------------------------------------------------------------------------- /MarcelloDB/AllocationStrategies/PredictiveBTreeNodeAllocationStrategy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using MarcelloDB.Index; 3 | using System.Reflection; 4 | 5 | namespace MarcelloDB.AllocationStrategies 6 | { 7 | internal class PredictiveBTreeNodeAllocationStrategy : IAllocationStrategy 8 | { 9 | Node Node { get; set; } 10 | 11 | internal PredictiveBTreeNodeAllocationStrategy(Node node) 12 | { 13 | this.Node = node; 14 | } 15 | 16 | #region IAllocationStrategy implementation 17 | 18 | public int CalculateSize(int dataSize) 19 | { 20 | if (Node.ChildrenAddresses.Count == 0 && Node.EntryList.Count == 0) 21 | { 22 | return dataSize; 23 | } 24 | 25 | var maxEntries = Index.Node.MaxEntriesForDegree(Node.Degree); 26 | 27 | if (Node.EntryList.Count > maxEntries) 28 | { 29 | throw new InvalidOperationException("PANIC: Too much entries in Node."); 30 | } 31 | var fillFactor = (float) maxEntries / (float)Node.EntryList.Count; 32 | 33 | var maxSizeGuesstimation = dataSize * fillFactor; 34 | 35 | if(!KeyIsValueType) 36 | { 37 | //Non-value types are hard to predict so we should give them some rooom to grow 38 | maxSizeGuesstimation *= 2; 39 | } 40 | 41 | return (int)maxSizeGuesstimation; 42 | } 43 | 44 | #endregion 45 | 46 | bool? _keyIsValueType; 47 | bool KeyIsValueType 48 | { 49 | get 50 | { 51 | if (!_keyIsValueType.HasValue) 52 | { 53 | _keyIsValueType = typeof(TK).GetTypeInfo().IsValueType; 54 | } 55 | return _keyIsValueType.Value; 56 | } 57 | } 58 | } 59 | } 60 | 61 | -------------------------------------------------------------------------------- /UniversalTest/UniversalTest.WindowsPhone/Package.appxmanifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 10 | 11 | UniversalTest.WindowsPhone 12 | markm_000 13 | Assets\StoreLogo.png 14 | 15 | 16 | 17 | 6.3.1 18 | 6.3.1 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 29 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /MarcelloDB.Test/TestStream/InMemoryStream.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using MarcelloDB.Storage; 5 | using System.Diagnostics; 6 | 7 | namespace MarcelloDB.Test 8 | { 9 | public class InMemoryStream : IStorageStream 10 | { 11 | public MemoryStream BackingStream { get; set; } 12 | public string Name {get; set; } 13 | public InMemoryStream(string name) 14 | { 15 | BackingStream = new MemoryStream (); 16 | this.Name = name; 17 | } 18 | 19 | #region IStorageStream implementation 20 | 21 | public byte[] Read (long address, int length) 22 | { 23 | if (length == 0) { 24 | } 25 | byte[] result = new byte[length]; 26 | BackingStream.Seek (address, SeekOrigin.Begin); 27 | BackingStream.Read (result, 0, length); 28 | return result; 29 | } 30 | 31 | public void Write (long address, byte[] bytes) 32 | { 33 | BackingStream.Seek (address, SeekOrigin.Begin); 34 | BackingStream.Write (bytes, 0, (int)bytes.Length); 35 | } 36 | 37 | #endregion 38 | } 39 | 40 | public class InMemoryStreamProvider : IStorageStreamProvider 41 | { 42 | Dictionary Streams { get; set;} 43 | 44 | public InMemoryStreamProvider(){ 45 | Streams = new Dictionary (); 46 | } 47 | 48 | #region IStorageStreamProvider implementation 49 | 50 | public IStorageStream GetStream (string streamName) 51 | { 52 | if(!Streams.ContainsKey(streamName)) 53 | { 54 | Streams.Add (streamName, new InMemoryStream (streamName)); 55 | } 56 | return Streams [streamName]; 57 | } 58 | 59 | #endregion 60 | 61 | public void Dispose() {} 62 | } 63 | } 64 | 65 | -------------------------------------------------------------------------------- /MarcelloDB/Collections/IndexedValueTypes/IndexedList.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace MarcelloDB.Collections 6 | { 7 | public class IndexedList : BaseIndexedValue 8 | { 9 | internal IndexedList(Func> valueFunction) 10 | :base((o)=>valueFunction(o).Distinct(), null) 11 | { 12 | } 13 | 14 | public IEnumerable Contains(TAttribute value) 15 | { 16 | return base.EqualsInternal(new TAttribute[]{value}); 17 | } 18 | 19 | public IEnumerable ContainsAny(IEnumerable values) 20 | { 21 | var enumerator = base.EqualsInternal(values); 22 | //enumerator will match the same object every time one of the values is in the index. 23 | //enumeratedAddresses tracks the enumerated addresses and filters the objects. 24 | enumerator.ShouldYieldObjectWithAddress = base.CreateDuplicateAddressFilter(); 25 | return enumerator; 26 | } 27 | 28 | public IEnumerable ContainsAll(IEnumerable values) 29 | { 30 | var enumerator = base.EqualsInternal(values); 31 | 32 | //enumerator will match the same object every time one of the values is in the index. 33 | //enumeratedAddresses tracks the enumerated addresses and filters the objects. 34 | enumerator.ShouldYieldObjectWithAddress = base.CreateDuplicateAddressFilter(); 35 | 36 | //enumerator will match any object with any value from the values parameter. 37 | enumerator.ShouldYieldObject = (o) => 38 | { 39 | var objectValues = ValueFunction(o).ToDictionary((obj)=>obj); 40 | return values.All((v)=> objectValues.ContainsKey(v)); 41 | }; 42 | return enumerator; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /MarcelloDB/Records/RecordHeader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using MarcelloDB.Serialization; 3 | 4 | namespace MarcelloDB.Records 5 | { 6 | internal class RecordHeader 7 | { 8 | const int BYTE_SIZE = sizeof(byte) + (2 * sizeof(Int32)); 9 | 10 | //Address is not stored 11 | internal byte HeaderVersion { get; set; } 12 | internal long Address { get; set; } 13 | internal Int32 DataSize { get; set; } 14 | internal Int32 AllocatedDataSize { get; set; } 15 | 16 | internal Int32 TotalRecordSize 17 | { 18 | get{ 19 | return ByteSize + AllocatedDataSize; 20 | } 21 | } 22 | 23 | static internal int ByteSize 24 | { 25 | get { 26 | return BYTE_SIZE; 27 | } 28 | } 29 | 30 | private RecordHeader(){} //construction only allowed from factory methods 31 | 32 | internal byte[] AsBytes() 33 | { 34 | var bytes = new byte[ByteSize]; 35 | var writer = new BufferWriter(bytes); 36 | writer 37 | .WriteByte(this.HeaderVersion) 38 | .WriteInt32(this.DataSize) 39 | .WriteInt32(this.AllocatedDataSize); 40 | return writer.GetTrimmedBuffer(); 41 | } 42 | 43 | #region factory methods 44 | 45 | internal static RecordHeader New (){ return new RecordHeader();} 46 | 47 | internal static RecordHeader FromBytes(Int64 address, byte[] bytes) 48 | { 49 | var reader = new BufferReader(bytes); 50 | var recordHeader = new RecordHeader(); 51 | recordHeader.Address = address; 52 | recordHeader.HeaderVersion = reader.ReadByte(); 53 | recordHeader.DataSize = reader.ReadInt32(); 54 | recordHeader.AllocatedDataSize = reader.ReadInt32(); 55 | return recordHeader; 56 | } 57 | 58 | #endregion 59 | } 60 | } 61 | 62 | -------------------------------------------------------------------------------- /MarcelloDB.netfx/MarcelloDB.netfx.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | AnyCPU 6 | {F62F30FD-5473-4427-BED9-6760B2C6C0FD} 7 | Library 8 | MarcelloDB.netfx 9 | MarcelloDB.netfx 10 | v4.5 11 | 12 | 13 | true 14 | full 15 | false 16 | bin\Debug 17 | DEBUG; 18 | prompt 19 | 4 20 | false 21 | 22 | 23 | full 24 | true 25 | bin\Release 26 | prompt 27 | 4 28 | false 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | {4172A119-2AE8-41D9-999E-9A2468267B28} 42 | MarcelloDB 43 | 44 | 45 | -------------------------------------------------------------------------------- /MarcelloDB/Index/Node.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace MarcelloDB.Index 5 | { 6 | internal class Node 7 | { 8 | public int Degree { get; set; } 9 | 10 | public Int64 Address{ get; set; } 11 | 12 | public AddressList ChildrenAddresses { get; set; } 13 | 14 | internal static int MaxEntriesForDegree(int degree) 15 | { 16 | return MaxChildrenForDegree(degree) - 1; 17 | } 18 | 19 | internal static int MaxChildrenForDegree(int degree) 20 | { 21 | return (2 * degree); 22 | } 23 | 24 | public Node(int degree) 25 | { 26 | this.Degree = degree; 27 | this.ChildrenAddresses = new AddressList(); 28 | } 29 | } 30 | 31 | internal class Node : Node 32 | { 33 | 34 | public EntryList EntryList { get; set; } 35 | 36 | public Node(int degree) : base(degree) 37 | { 38 | this.EntryList = new EntryList(); 39 | } 40 | 41 | internal bool IsLeaf 42 | { 43 | get 44 | { 45 | return this.ChildrenAddresses.Count == 0; 46 | } 47 | } 48 | 49 | internal bool HasReachedMaxEntries 50 | { 51 | get 52 | { 53 | return this.EntryList.Count == MaxEntriesForDegree(this.Degree); 54 | } 55 | } 56 | 57 | internal bool HasReachedMinEntries 58 | { 59 | get 60 | { 61 | return this.EntryList.Count == this.Degree - 1; 62 | } 63 | } 64 | 65 | internal bool Dirty 66 | { 67 | get 68 | { 69 | return ChildrenAddresses.Dirty || EntryList.Dirty; 70 | } 71 | } 72 | 73 | internal void ClearChanges() 74 | { 75 | ChildrenAddresses.ClearChanges(); 76 | EntryList.ClearChanges(); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /MarcelloDB.Test/Serialization/TransactionJournalSerializerTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using MarcelloDB.Serialization; 4 | using MarcelloDB.Transactions; 5 | 6 | namespace MarcelloDB.Test.Serialization 7 | { 8 | [TestFixture] 9 | public class TransactionJournalSerializerTest 10 | { 11 | TransactionJournalSerializer _serializer; 12 | 13 | [SetUp] 14 | public void Initialize() 15 | { 16 | _serializer = new TransactionJournalSerializer(); 17 | } 18 | 19 | [Test] 20 | public void SerializesTansactionJournal() 21 | { 22 | var journal = new TransactionJournal(); 23 | journal.Entries.Add(new JournalEntry 24 | { 25 | Address = 1, 26 | Data = new byte[]{ 2, 3, 4 }, 27 | Stamp = 5, 28 | StreamName = "TestStream 1" 29 | }); 30 | 31 | journal.Entries.Add(new JournalEntry 32 | { 33 | Address = 6, 34 | Data = new byte[]{ 7, 8, 9 }, 35 | Stamp = 10, 36 | StreamName = "TestStream 2" 37 | }); 38 | 39 | var deserialized = _serializer.Deserialize(_serializer.Serialize(journal)); 40 | Assert.AreEqual(2, deserialized.Entries.Count); 41 | 42 | Assert.AreEqual(1, deserialized.Entries[0].Address); 43 | Assert.AreEqual(new byte[]{2, 3, 4}, deserialized.Entries[0].Data); 44 | Assert.AreEqual(5, deserialized.Entries[0].Stamp); 45 | Assert.AreEqual("TestStream 1", deserialized.Entries[0].StreamName); 46 | 47 | Assert.AreEqual(6, deserialized.Entries[1].Address); 48 | Assert.AreEqual(new byte[]{7, 8, 9}, deserialized.Entries[1].Data); 49 | Assert.AreEqual(10, deserialized.Entries[1].Stamp); 50 | Assert.AreEqual("TestStream 2", deserialized.Entries[1].StreamName); 51 | } 52 | } 53 | } 54 | 55 | -------------------------------------------------------------------------------- /MarcelloDB.Test/Classes/ArticleIndexDefinition.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using MarcelloDB.Index; 3 | using MarcelloDB.Collections; 4 | 5 | namespace MarcelloDB.Test.Classes 6 | { 7 | class ArticleIndexes : IndexDefinition
8 | { 9 | public IndexedValue Name { get; set;} 10 | 11 | public IndexedValue Description { get; set; } 12 | 13 | public IndexedValue Category { get; set; } 14 | 15 | public UniqueIndexedValue Reference { get; set; } 16 | 17 | public UniqueIndexedValue CustomReference 18 | { 19 | get 20 | { 21 | return UniqueIndexedValue(a => a.Reference); 22 | } 23 | } 24 | 25 | public IndexedValue FullDescription 26 | { 27 | get 28 | { 29 | return IndexedValue( 30 | article => String.Format("{0}-{1}", article.Name, article.Description) 31 | ); 32 | } 33 | } 34 | 35 | public IndexedValue CodeAndName 36 | { 37 | get 38 | { 39 | return IndexedValue( 40 | article =>CompoundValue(article.Code, article.Name) 41 | ); 42 | } 43 | } 44 | 45 | public IndexedValue IdCodeAndName 46 | { 47 | get 48 | { 49 | return IndexedValue( 50 | article => CompoundValue(article.ID, article.Code, article.Name) 51 | ); 52 | } 53 | } 54 | 55 | public IndexedValue ShortName 56 | { 57 | get 58 | { 59 | return IndexedValue( 60 | article => article.ShortName, 61 | article => article.ShortName != null 62 | ); 63 | } 64 | } 65 | } 66 | } 67 | 68 | -------------------------------------------------------------------------------- /MarcelloDB/Index/CompoundValue.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Collections.Generic; 4 | 5 | namespace MarcelloDB.Index 6 | { 7 | public abstract partial class CompoundValue : IComparable 8 | { 9 | internal abstract IEnumerable GetValues(); 10 | 11 | internal CompoundValue(){} 12 | 13 | #region IComparable implementation 14 | public int CompareTo(object objB) 15 | { 16 | var valuesA = GetValues(); 17 | var valuesB = ((CompoundValue)objB).GetValues(); 18 | return CompareValues(valuesA, valuesB); 19 | } 20 | #endregion 21 | 22 | public override bool Equals(object objB) 23 | { 24 | return this.CompareTo(objB) == 0; 25 | } 26 | 27 | public override int GetHashCode() 28 | { 29 | return base.GetHashCode(); 30 | } 31 | 32 | int CompareValues(IEnumerable valuesA, IEnumerable valuesB) 33 | { 34 | if (valuesA.Count() == 0 || valuesB.Count() == 0) 35 | return 0; 36 | 37 | if (valuesB.Count() == 0) 38 | return 1; 39 | 40 | var firstA = valuesA.First(); 41 | var firstB = valuesB.First(); 42 | var compareResult = CompareValue(firstA, firstB); 43 | if (compareResult == 0) 44 | { 45 | return CompareValues(valuesA.Skip(1), valuesB.Skip(1)); 46 | } 47 | return compareResult; 48 | 49 | } 50 | 51 | int CompareValue(object valA, object valB) 52 | { 53 | if (valA != null && valB == null) 54 | { 55 | return 1; 56 | } 57 | if (valA == null && valB != null) 58 | { 59 | return -1; 60 | } 61 | if (valA == null && valB == null) 62 | { 63 | return 0; 64 | } 65 | return ((IComparable)valA).CompareTo(valB); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | [![Build Status](https://travis-ci.org/markmeeus/MarcelloDB.svg?branch=master)](https://travis-ci.org/markmeeus/MarcelloDB) 5 | 6 | MarcelloDB is an embedded NoSql object-database for Xamarin and UWP (Windows Universal) apps. 7 | 8 | MarcelloDB saves plain C# objects, including child objects, lists and dictionaries. 9 | Not having to map your objects to the relational model can save you hundreds of lines of code. 10 | 11 | It's a pure C# implementation, no need to package other binaries. 12 | 13 | #Documentation 14 | Read the docs here: [http://www.marcellodb.org](http://www.marcellodb.org) 15 | 16 | #Current Status 17 | Current version is 1.0.7. 18 | 19 | Although it's the first version, the community has been testing beta versions since october 2015. It is allready really stable. 20 | 21 | 22 | ###Installation 23 | ```cs 24 | PM > Install-Package MarcelloDB 25 | ``` 26 | 27 | ###A simple console app to get you started. 28 | 29 | ```cs 30 | 31 | public class Book{ 32 | public string BookId { get; set; } 33 | public string Title { get; set; } 34 | } 35 | 36 | class MainClass 37 | { 38 | public static void Main (string[] args) 39 | { 40 | var platform = new MarcelloDB.netfx.Platform(); 41 | var session = new MarcelloDB.Session(platform, "."); 42 | 43 | var productsFile = session["products.data"]; 44 | 45 | var bookCollection = productsFile.Collection("books", book => book.BookId); 46 | 47 | var newBook = new Book{ BookId = "123", Title = "The Girl With The Dragon Tattoo" }; 48 | 49 | bookCollection.Persist(newBook); 50 | 51 | foreach(var book in bookCollection.All) 52 | { 53 | Console.WriteLine(book.Title); 54 | } 55 | 56 | var theBook = bookCollection.Find("123"); 57 | 58 | Console.WriteLine("Found book: " + theBook.Title); 59 | 60 | bookCollection.Destroy("123"); 61 | } 62 | } 63 | 64 | ``` 65 | ### Learn more: www.marcellodb.org 66 | -------------------------------------------------------------------------------- /MarcelloDB/Collections/KeysEnumeratorT.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using MarcelloDB.Collections; 3 | using MarcelloDB.Index; 4 | using MarcelloDB.Index.BTree; 5 | using System.Collections.Generic; 6 | using System.Collections; 7 | 8 | namespace MarcelloDB 9 | { 10 | internal class KeysEnumerator : SessionBoundObject, IEnumerable 11 | { 12 | BTreeWalkerRange Range { get; set; } 13 | 14 | Collection Collection { get; set; } 15 | 16 | RecordIndex Index { get; set; } 17 | 18 | bool HasRange{ get { return this.Range != null; } } 19 | 20 | bool IsDescending { get; set; } 21 | 22 | internal KeysEnumerator( 23 | Collection collection, 24 | Session session, 25 | RecordIndex index, 26 | bool isDescending = false 27 | ) : base(session) 28 | { 29 | this.Collection = collection; 30 | this.Index = index; 31 | this.IsDescending = isDescending; 32 | } 33 | 34 | internal void SetRange(BTreeWalkerRange range) 35 | { 36 | this.Range = range; 37 | } 38 | 39 | #region IEnumerable implementation 40 | IEnumerator IEnumerable.GetEnumerator() 41 | { 42 | lock (this.Session.SyncLock) 43 | { 44 | var indexEnumerator = new IndexEntryEnumerator( 45 | this.Collection, 46 | this.Session, 47 | this.Index, 48 | this.IsDescending); 49 | 50 | indexEnumerator.SetRange(this.Range); 51 | 52 | foreach (var node in indexEnumerator) 53 | { 54 | yield return node.Key; 55 | } 56 | } 57 | } 58 | #endregion 59 | #region IEnumerable implementation 60 | IEnumerator IEnumerable.GetEnumerator() 61 | { 62 | return ((IEnumerable)this).GetEnumerator(); 63 | } 64 | #endregion 65 | } 66 | } 67 | 68 | -------------------------------------------------------------------------------- /docs/reading.md: -------------------------------------------------------------------------------- 1 | ## Finding an object 2 | MarcelloDB keeps track of all objects in a collection using it's ID. The ID's of a collection are stored in a BTree structure inside the same collection-file. 3 | 4 | Finding an object by it's ID is very performant, even for very large collections. 5 | 6 | ```cs 7 | bookCollection.Find(123); 8 | ``` 9 | 10 | The Find method uses the correct type for the ID as parameter. This way, the compiler will protect you from trivial type mistakes. 11 | 12 | 13 | ##Enumerating a collection 14 | 15 | Using the collections All property you can enumerate all objects in the collection. 16 | 17 | ```cs 18 | foreach(var book in bookCollection.All){} 19 | ``` 20 | MarcelloDB loads only the current object in memory, so even for large collections, enumerating would be quite safe to do memory-wise. 21 | 22 | > To avoid data corruption, enumerating a collection locks the collection for other threads write operations untill the enumeration is done. 23 | 24 | > MarcelloDB will throw an exception when the enumerating thread tries to write. 25 | 26 | ## A note about Linq 27 | Since All is an IEnumerable<T> (T is the type of your objects) you can use Linq on it. 28 | 29 | However, there is no such thing as Linq-To-MarcelloDB. Some Linq expressions will still enumerate all objects. Which may be fine for a small collection. 30 | 31 | For instance, to get all thrillers from a book collection, the following code will enumerate all objects. 32 | Which is fine for a small collection. 33 | ```cs 34 | var thrillers = bookCollection.All.Where(b => b.Category == Categories.Thriller); 35 | ``` 36 | 37 | However if we use an index, we'll enumerate only the objects we actually want. 38 | [(More about indexes ...)](indexes.html) 39 | ```cs 40 | var thrillers = bookCollection.Indexes.Category.Equals(Categories.Thriller); 41 | ``` 42 | 43 | ###Why no Linq-To-MarcelloDB 44 | Linq-To-MarcelloDB would certainly be possible. The expression could be evaluated and when an index exists, it could be used. 45 | However, the philosophy of MarcelloDB is to be always explicit, allowing the compiler to protect us as much as possible. 46 | Using this expressive api, it is obvious that an index is used. No need for debugging here. 47 | -------------------------------------------------------------------------------- /MarcelloDB/Serialization/SerializerResolver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using MarcelloDB.Records; 3 | using MarcelloDB.Index; 4 | using System.Collections.Generic; 5 | using System.Reflection; 6 | using System.Linq; 7 | using MarcelloDB.Transactions; 8 | 9 | namespace MarcelloDB.Serialization 10 | { 11 | internal class SerializerResolver 12 | { 13 | Dictionary _serializers; 14 | 15 | internal SerializerResolver() 16 | { 17 | _serializers = new Dictionary { 18 | {typeof(IndexMetaRecord), new IndexMetaRecordSerializer()}, 19 | {typeof(Node), new EmptyRecordIndexNodeSerializer()}, 20 | {typeof(TransactionJournal), new TransactionJournalSerializer()}, 21 | {typeof(CollectionFileRoot), new CollectionFileRootSerializer()}, 22 | }; 23 | } 24 | 25 | internal IObjectSerializer SerializerFor() 26 | { 27 | if (!_serializers.ContainsKey(typeof(T))) 28 | { 29 | _serializers[typeof(T)] = ConstructSerializer(); 30 | } 31 | return (IObjectSerializer)_serializers[typeof(T)]; 32 | } 33 | 34 | IObjectSerializer ConstructSerializer() 35 | { 36 | if(typeof(T).GetTypeInfo().IsSubclassOf(typeof(Node))) 37 | { 38 | return ConstructBTreeNodeSerializer(); 39 | } 40 | return new BsonSerializer(); 41 | } 42 | 43 | IObjectSerializer ConstructBTreeNodeSerializer() 44 | { 45 | var genericTypes = typeof(T).GenericTypeArguments; 46 | TypeInfo typeInfo; 47 | if (BTreeNodeBinaryFormatterSerializer.CanSerialize(typeof(T))) 48 | { 49 | typeInfo = typeof(BTreeNodeBinaryFormatterSerializer<>).GetTypeInfo(); 50 | } 51 | else 52 | { 53 | typeInfo = typeof(BTreeNodeBsonSerializer<>).GetTypeInfo(); 54 | } 55 | 56 | var genericType = typeInfo.MakeGenericType(genericTypes); 57 | 58 | return (IObjectSerializer)Activator.CreateInstance (genericType); 59 | } 60 | } 61 | } 62 | 63 | -------------------------------------------------------------------------------- /MarcelloDB/Index/ObjectComparer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MarcelloDB.Index 4 | { 5 | internal class ObjectComparer 6 | { 7 | bool Inverted { get; set; } 8 | 9 | public ObjectComparer() 10 | { 11 | } 12 | 13 | public int Compare(object a, object b) 14 | { 15 | long lngA; 16 | long lngB; 17 | 18 | if(TryLongify(a, out lngA) && TryLongify(b, out lngB)) 19 | { 20 | return InvertIfNecessary( 21 | lngA.CompareTo(lngB) 22 | ); 23 | } 24 | 25 | if(a is DateTime) { 26 | return CompareDateTimes((DateTime)a, (DateTime)b); 27 | } 28 | 29 | return InvertIfNecessary( 30 | ((IComparable)a).CompareTo((IComparable)b) 31 | ); 32 | } 33 | 34 | public void Invert() 35 | { 36 | this.Inverted = true; 37 | } 38 | 39 | int InvertIfNecessary(int compareValue) 40 | { 41 | if (this.Inverted) 42 | { 43 | if (compareValue < 0) 44 | return 1; 45 | if (compareValue > 0) 46 | return -1; 47 | } 48 | return compareValue; 49 | } 50 | bool TryLongify(object o, out long result) 51 | { 52 | if (o is short) 53 | { 54 | result = (long)(short)o; 55 | return true; 56 | } 57 | if (o is int) 58 | { 59 | result = (long)(int)o; 60 | return true; 61 | } 62 | if (o is long) 63 | { 64 | result = (long)o; 65 | return true; 66 | } 67 | 68 | result = 0; 69 | return false; 70 | } 71 | 72 | int CompareDateTimes(DateTime a, DateTime b) 73 | { 74 | var aToSecondPrecission = a.AddTicks(-a.Ticks % (TimeSpan.TicksPerSecond / 1000)); 75 | var bToSecondPrescission = b.AddTicks(-b.Ticks % (TimeSpan.TicksPerSecond / 1000)); 76 | return aToSecondPrecission.CompareTo(bToSecondPrescission); 77 | } 78 | } 79 | } 80 | 81 | -------------------------------------------------------------------------------- /MarcelloDB.Test/Index/ObjectComparerTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using MarcelloDB.Index; 3 | using NUnit.Framework; 4 | 5 | namespace MarcelloDB.Test.Index 6 | { 7 | [TestFixture] 8 | public class ObjectComparerTest 9 | { 10 | ObjectComparer _comparer; 11 | 12 | [SetUp] 13 | public void Initialize() 14 | { 15 | _comparer = new ObjectComparer(); 16 | } 17 | 18 | [Test] 19 | public void Compares_Shorts() 20 | { 21 | Assert.AreEqual(-1, _comparer.Compare((short)1, (short)2)); 22 | Assert.AreEqual(0, _comparer.Compare((short)2, (short)2)); 23 | Assert.AreEqual(1, _comparer.Compare((short)2, (short)1)); 24 | } 25 | 26 | [Test] 27 | public void Compares_Integers() 28 | { 29 | Assert.AreEqual(-1, _comparer.Compare((int)1, (int)2)); 30 | Assert.AreEqual(0, _comparer.Compare((int)2, (int)2)); 31 | Assert.AreEqual(1, _comparer.Compare((int)2, (int)1)); 32 | } 33 | 34 | [Test] 35 | public void Compares_Longs() 36 | { 37 | Assert.AreEqual(-1, _comparer.Compare((long)1, (long)2)); 38 | Assert.AreEqual(0, _comparer.Compare((long)2, (long)2)); 39 | Assert.AreEqual(1, _comparer.Compare((long)2, (long)1)); 40 | } 41 | 42 | [Test] 43 | public void Compares_Strings() 44 | { 45 | Assert.AreEqual(-1, _comparer.Compare("1", "2")); 46 | Assert.AreEqual(0, _comparer.Compare("2", "2")); 47 | Assert.AreEqual(1, _comparer.Compare("2", "1")); 48 | } 49 | 50 | [Test] 51 | public void ComparesInverted() 52 | { 53 | _comparer.Invert(); 54 | Assert.AreEqual(1, _comparer.Compare((short)1, (short)2)); 55 | Assert.AreEqual(0, _comparer.Compare((short)2, (short)2)); 56 | Assert.AreEqual(-1, _comparer.Compare((short)2, (short)1)); 57 | } 58 | 59 | [Test] 60 | public void Compares_DateTime_With_MillisecondPrecision() 61 | { 62 | var date = new DateTime(2017,01,01); 63 | var datePlus1Tick = new DateTime(date.Ticks + 1); 64 | Assert.AreEqual(0, _comparer.Compare(date, datePlus1Tick)); 65 | } 66 | } 67 | } 68 | 69 | -------------------------------------------------------------------------------- /MarcelloDB.Test/Regression/Issue49_DateTime_Indexed_Value.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using MarcelloDB.Collections; 6 | using MarcelloDB.Index; 7 | using MarcelloDB.Platform; 8 | using NUnit.Framework; 9 | 10 | namespace MarcelloDB.Test.Regression.Issue49 11 | { 12 | public class Product 13 | { 14 | public Guid Id { get; set; } 15 | public DateTime Date { get; set; } 16 | 17 | public Product () 18 | { 19 | this.Id = Guid.NewGuid(); 20 | this.Date = DateTime.Now; 21 | } 22 | } 23 | 24 | public class ProductIndexDefinition : IndexDefinition 25 | { 26 | public IndexedValue Date { get; set; } 27 | } 28 | 29 | [TestFixture] 30 | public class DateTime_Indexed_Value 31 | { 32 | Session _session; 33 | Collection _collection; 34 | IPlatform _platform; 35 | [SetUp] 36 | public void Setup () 37 | { 38 | _platform = new TestPlatform(); 39 | _session = new Session (_platform, ""); 40 | _collection = _session["moves.dat"].Collection 41 | ("products", p => p.Id); 42 | } 43 | 44 | [Test] 45 | public void Second_Persist_Should_Not_Crash () 46 | { 47 | //comparing now 48 | var item = new Product(); 49 | var id = item.Id; 50 | _collection.Persist(item); 51 | 52 | //lLoad from DB and persist againg 53 | var productFromDB = _collection.Find(id); 54 | 55 | //Date property now has a value with ticks rounded to milliseconds 56 | //The index has microsecond precision 57 | //after persisting this record again, there are 2 entries for this record 58 | //One with microsecond precision, and one with millisecond precision 59 | _collection.Persist (productFromDB); 60 | 61 | //Persiting will first load the original to unregister index entries 62 | //The millisecond precision one will be removed 63 | //Registering will use the higher precision one, which is still in de index and causes the error 64 | _collection.Persist(item); 65 | } 66 | 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /MarcelloDB/Records/CollectionFileRoot.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using MarcelloDB.Records; 4 | using MarcelloDB.Transactions; 5 | using MarcelloDB.Serialization; 6 | 7 | namespace MarcelloDB 8 | { 9 | internal class CollectionFileRoot 10 | { 11 | // first Int64 points to the collection file root record 12 | internal const int INITIAL_HEAD = sizeof(Int64); 13 | 14 | //Defines the format of file structure. Only change this value if backwards compatibility is broken. 15 | internal const int CURRENT_FORMAT_VERSION = 2; 16 | 17 | public int FormatVersion { get; set; } 18 | 19 | internal bool IsDirty { get; private set;} 20 | 21 | internal CollectionFileRoot() 22 | { 23 | this.Head = INITIAL_HEAD; 24 | this.FormatVersion = CURRENT_FORMAT_VERSION; 25 | this.IsDirty = true; 26 | } 27 | 28 | public static CollectionFileRoot Create() 29 | { 30 | return new CollectionFileRoot(); 31 | } 32 | 33 | Int64 _head; 34 | public Int64 Head { 35 | get { return _head; } 36 | set 37 | { 38 | if (_head != value) 39 | { 40 | _head = value; 41 | this.IsDirty = true; 42 | } 43 | } 44 | } 45 | 46 | Int64 _namedRecordIndexAddress; 47 | public Int64 NamedRecordIndexAddress { 48 | get { return _namedRecordIndexAddress; } 49 | set 50 | { 51 | if (_namedRecordIndexAddress != value) 52 | { 53 | _namedRecordIndexAddress = value; 54 | this.IsDirty = true; 55 | } 56 | } 57 | } 58 | 59 | internal void Validate() 60 | { 61 | if (this.FormatVersion != CURRENT_FORMAT_VERSION) 62 | { 63 | throw new InvalidOperationException( 64 | string.Format("This collectionfile was created with a previous and unsupported version of MarcellodB. " + 65 | "More info: https://github.com/markmeeus/MarcelloDB") 66 | ); 67 | } 68 | } 69 | 70 | internal void Clean() 71 | { 72 | this.IsDirty = false; 73 | } 74 | } 75 | } 76 | 77 | -------------------------------------------------------------------------------- /MarcelloDB/Records/IndexMetaRecord.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MarcelloDB.Records 4 | { 5 | internal class IndexMetaRecord 6 | { 7 | Int64 _rootNodeAddress; 8 | internal Int64 RootNodeAddress { 9 | get { return _rootNodeAddress; } 10 | set { 11 | if (_rootNodeAddress != value) 12 | { 13 | _rootNodeAddress = value; 14 | this.IsDirty = true; 15 | } 16 | } 17 | } 18 | 19 | Int64 _numberOfNodes; 20 | internal Int64 NumberOfNodes { 21 | get { return _numberOfNodes;} 22 | set 23 | { 24 | if (_numberOfNodes != value) 25 | { 26 | _numberOfNodes = value; 27 | this.IsDirty = true; 28 | } 29 | } 30 | } 31 | 32 | //not used yet 33 | Int64 _numberOfEntries; 34 | internal Int64 NumberOfEntries 35 | { 36 | get {return _numberOfEntries; } 37 | set 38 | { 39 | if (_numberOfEntries != value) 40 | { 41 | _numberOfEntries = value; 42 | this.IsDirty = true; 43 | } 44 | } 45 | } 46 | 47 | Int64 _totalAllocatedSize; 48 | internal Int64 TotalAllocatedSize 49 | { 50 | get {return _totalAllocatedSize; } 51 | set 52 | { 53 | if (_totalAllocatedSize != value) 54 | { 55 | _totalAllocatedSize = value; 56 | this.IsDirty = true; 57 | } 58 | } 59 | } 60 | 61 | Int64 _totalAllocatedDataSize; 62 | internal Int64 TotalAllocatedDataSize 63 | { 64 | get {return _totalAllocatedDataSize; } 65 | set 66 | { 67 | if (_totalAllocatedDataSize != value) 68 | { 69 | _totalAllocatedDataSize = value; 70 | this.IsDirty = true; 71 | } 72 | } 73 | } 74 | 75 | internal Record Record { get; set; } 76 | 77 | internal bool IsDirty { get; private set; } 78 | 79 | internal void Clean() 80 | { 81 | this.IsDirty = false; 82 | } 83 | } 84 | } 85 | 86 | -------------------------------------------------------------------------------- /MarcelloDB/Transactions/Transaction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using System.Collections.Generic; 4 | 5 | namespace MarcelloDB.Transactions 6 | { 7 | internal class Transaction : SessionBoundObject 8 | { 9 | internal bool Running { get; set; } 10 | 11 | internal bool IsCommitting { get; set; } 12 | 13 | List Transactors { get; set; } 14 | 15 | int Enlisted { get; set; } 16 | 17 | internal Transaction(Session session) : base(session) 18 | { 19 | Transactors = new List(); 20 | this.Running = true; 21 | this.IsCommitting = false; 22 | } 23 | 24 | internal void Enlist() 25 | { 26 | if (this.IsCommitting) 27 | return; 28 | 29 | this.Enlisted++; 30 | } 31 | 32 | internal void AddTransactor(ITransactor transactor) 33 | { 34 | if(!Transactors.Contains (transactor)){ 35 | Transactors.Add (transactor); 36 | } 37 | } 38 | 39 | internal void Leave() 40 | { 41 | if (this.IsCommitting) 42 | return; 43 | 44 | this.Enlisted--; 45 | 46 | if (this.Enlisted == 0) 47 | { 48 | this.Commit(); 49 | this.Running = false; 50 | } 51 | } 52 | 53 | internal void Rollback() 54 | { 55 | Session.Journal.ClearUncommitted(); 56 | foreach (var transactor in Transactors) 57 | { 58 | transactor.RollbackState(); 59 | } 60 | this.Running = false; 61 | } 62 | 63 | internal void Commit() 64 | { 65 | this.IsCommitting = true; 66 | 67 | foreach (var transactor in Transactors) 68 | { 69 | transactor.SaveState(); 70 | } 71 | 72 | Session.Journal.Commit(); 73 | 74 | this.IsCommitting = false; 75 | this.TryApply(); 76 | } 77 | 78 | void Apply() 79 | { 80 | lock (this.Session.SyncLock) 81 | { 82 | Session.Journal.Apply(); 83 | } 84 | } 85 | 86 | void TryApply() 87 | { 88 | try{ 89 | Apply(); 90 | }catch(Exception){} 91 | } 92 | } 93 | } 94 | 95 | -------------------------------------------------------------------------------- /MarcelloDB.Test/Records/ValueWithAddressIndexKeyTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using MarcelloDB.Serialization; 4 | using MarcelloDB.Records; 5 | 6 | namespace MarcelloDB.Test.Records 7 | { 8 | [TestFixture] 9 | public class ValueWithAddressIndexKeyTest 10 | { 11 | [SetUp] 12 | public void Initialize() 13 | { 14 | } 15 | 16 | [Test] 17 | public void Can_Be_Serialized() 18 | { 19 | var key = new ValueWithAddressIndexKey{A = 10, V = 20 }; 20 | var serializer = new BsonSerializer>(); 21 | var deserialized = serializer.Deserialize( 22 | serializer.Serialize(key) 23 | ); 24 | Assert.AreEqual(key.A, deserialized.A); 25 | Assert.AreEqual(key.V, deserialized.V); 26 | } 27 | 28 | [Test] 29 | public void Compares_On_Value_First() 30 | { 31 | var key1 = new ValueWithAddressIndexKey{V = 1, A = 2 }; 32 | var key2 = new ValueWithAddressIndexKey{V = 2, A = 1 }; 33 | Assert.IsTrue(key1.CompareTo(key2) < 0); 34 | } 35 | 36 | [Test] 37 | public void Compares_On_Address_When_Size_Is_Equal() 38 | { 39 | var key1 = new ValueWithAddressIndexKey{V = 1, A = 1 }; 40 | var key2 = new ValueWithAddressIndexKey{V = 1, A = 2 }; 41 | Assert.IsTrue(key1.CompareTo(key2) < 0); 42 | } 43 | [Test] 44 | public void Considers_Equal_When_Address_Missing() 45 | { 46 | var key1 = new ValueWithAddressIndexKey{V = 1, A = 1 }; 47 | var key2 = new ValueWithAddressIndexKey{V = 1}; 48 | Assert.AreEqual(0, key1.CompareTo(key2)); 49 | Assert.AreEqual(0, key2.CompareTo(key1)); 50 | Assert.IsTrue(key1.Equals(key2)); 51 | Assert.IsTrue(key2.Equals(key1)); 52 | } 53 | 54 | [Test] 55 | public void Compares_DateTimes_With_Millisecond_Precision() 56 | { 57 | var date = new DateTime(2017, 01, 01); 58 | var datePlus1Tick = new DateTime(date.Ticks + 1); 59 | var key1 = new ValueWithAddressIndexKey { V = date, A = 1 }; 60 | var key2 = new ValueWithAddressIndexKey { V = datePlus1Tick, A = 1 }; 61 | Assert.AreEqual(0, key1.CompareTo(key2)); 62 | 63 | } 64 | } 65 | } 66 | 67 | -------------------------------------------------------------------------------- /MarcelloDB.Test/Serialization/IndexMetaRecordSerializerTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using MarcelloDB.Records; 4 | using MarcelloDB.Serialization; 5 | 6 | namespace MarcelloDB.Test.Serialization 7 | { 8 | [TestFixture] 9 | public class IndexMetaRecordSerializerTest 10 | { 11 | IndexMetaRecord _record; 12 | IndexMetaRecord _deserializedRecord; 13 | 14 | [SetUp] 15 | public void Initialize() 16 | { 17 | _record = new IndexMetaRecord 18 | { 19 | RootNodeAddress = 1, 20 | NumberOfNodes = 2, 21 | NumberOfEntries = 3, 22 | TotalAllocatedDataSize = 4, 23 | TotalAllocatedSize = 5 24 | }; 25 | 26 | var serializer = new IndexMetaRecordSerializer(); 27 | _deserializedRecord = serializer.Deserialize( 28 | serializer.Serialize(_record)); 29 | } 30 | 31 | [Test] 32 | public void Serializes_RootRecordAddress() 33 | { 34 | Assert.AreEqual(_record.RootNodeAddress, _deserializedRecord.RootNodeAddress); 35 | } 36 | 37 | [Test] 38 | public void Serializes_NumberOfEntries() 39 | { 40 | Assert.AreEqual(_record.NumberOfEntries, _deserializedRecord.NumberOfEntries); 41 | } 42 | 43 | [Test] 44 | public void Serializes_NumberOfNodes() 45 | { 46 | Assert.AreEqual(_record.NumberOfNodes, _deserializedRecord.NumberOfNodes); 47 | } 48 | 49 | [Test] 50 | public void Serializes_TotalAllocatedSize() 51 | { 52 | Assert.AreEqual(_record.TotalAllocatedSize, _deserializedRecord.TotalAllocatedSize); 53 | } 54 | 55 | [Test] 56 | public void Serializes_TotalAllocatedDataSize() 57 | { 58 | Assert.AreEqual(_record.TotalAllocatedDataSize, _deserializedRecord.TotalAllocatedDataSize); 59 | } 60 | 61 | [Test] 62 | public void Deserialized_Should_Not_Be_Dirty() 63 | { 64 | var record = new IndexMetaRecord(); 65 | record.RootNodeAddress = 1; 66 | 67 | var serializer = new IndexMetaRecordSerializer(); 68 | var deserializedRecord = serializer.Deserialize( 69 | serializer.Serialize(_record)); 70 | 71 | Assert.IsFalse(deserializedRecord.IsDirty); 72 | } 73 | } 74 | } 75 | 76 | -------------------------------------------------------------------------------- /MarcelloDB/Serialization/BufferReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MarcelloDB.Serialization 4 | { 5 | internal class BufferReader 6 | { 7 | byte[] Buffer { get; set; } 8 | 9 | int Position {get;set;} 10 | 11 | bool IsLittleEndian {get;set;} 12 | 13 | internal BufferReader(byte[] buffer) 14 | { 15 | Initialize(buffer, BitConverter.IsLittleEndian); 16 | } 17 | 18 | internal BufferReader(byte[] buffer, bool isLittleEndian) 19 | { 20 | Initialize(buffer, isLittleEndian); 21 | } 22 | 23 | internal byte ReadByte() 24 | { 25 | var value = this.Buffer[this.Position]; 26 | this.Position++; 27 | return value; 28 | } 29 | 30 | internal byte[] ReadBytes(int length) 31 | { 32 | var bytes = new byte[length]; 33 | Array.Copy(this.Buffer, this.Position, bytes, 0, bytes.Length); 34 | this.Position += length; 35 | return bytes; 36 | } 37 | 38 | public Int16 ReadInt16() 39 | { 40 | var bytes = GetBytesInLittleEndianOrder(sizeof(Int16)); 41 | 42 | var value = BitConverter.ToInt16(bytes, 0); 43 | this.Position += sizeof(Int16); 44 | return value; 45 | } 46 | 47 | internal Int32 ReadInt32() 48 | { 49 | var bytes = GetBytesInLittleEndianOrder(sizeof(Int32)); 50 | 51 | var value = BitConverter.ToInt32(bytes, 0); 52 | this.Position += sizeof(Int32); 53 | return value; 54 | } 55 | 56 | internal Int64 ReadInt64() 57 | { 58 | var bytes = GetBytesInLittleEndianOrder(sizeof(Int64)); 59 | 60 | var value = BitConverter.ToInt64(bytes, 0); 61 | this.Position += sizeof(Int64); 62 | return value; 63 | } 64 | 65 | void Initialize(byte[] buffer, bool isLittleEndian){ 66 | this.Buffer = buffer; 67 | this.Position = 0; 68 | this.IsLittleEndian = isLittleEndian; 69 | } 70 | 71 | byte[] GetBytesInLittleEndianOrder(int size) 72 | { 73 | var bytes = new byte[size]; 74 | Array.Copy(this.Buffer, this.Position, bytes, 0, bytes.Length); 75 | if (!this.IsLittleEndian) 76 | { 77 | Array.Reverse(bytes); 78 | } 79 | return bytes; 80 | } 81 | } 82 | } -------------------------------------------------------------------------------- /MarcelloDB/Serialization/ValueSerializers/IntSerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MarcelloDB.Serialization.ValueSerializers 4 | { 5 | class Int16Serializer : ValueSerializer 6 | { 7 | internal override Int16 ReadValue(BinaryFormatter formatter) 8 | { 9 | return formatter.ReadInt16(); 10 | } 11 | internal override void WriteValue(BinaryFormatter formatter, Int16 value) 12 | { 13 | formatter.WriteInt16(value); 14 | } 15 | } 16 | 17 | class NullableInt16Serializer : ValueSerializer 18 | { 19 | internal override Int16? ReadValue(BinaryFormatter formatter) 20 | { 21 | return formatter.ReadNullableInt16(); 22 | } 23 | internal override void WriteValue(BinaryFormatter formatter, Int16? value) 24 | { 25 | formatter.WriteNullableInt16(value); 26 | } 27 | } 28 | 29 | class Int32Serializer : ValueSerializer 30 | { 31 | internal override Int32 ReadValue(BinaryFormatter formatter) 32 | { 33 | return formatter.ReadInt32(); 34 | } 35 | internal override void WriteValue(BinaryFormatter formatter, Int32 value) 36 | { 37 | formatter.WriteInt32(value); 38 | } 39 | } 40 | 41 | class NullableInt32Serializer : ValueSerializer 42 | { 43 | internal override Int32? ReadValue(BinaryFormatter formatter) 44 | { 45 | return formatter.ReadNullableInt32(); 46 | } 47 | internal override void WriteValue(BinaryFormatter formatter, Int32? value) 48 | { 49 | formatter.WriteNullableInt32(value); 50 | } 51 | } 52 | 53 | class Int64Serializer : ValueSerializer 54 | { 55 | internal override Int64 ReadValue(BinaryFormatter formatter) 56 | { 57 | return formatter.ReadInt64(); 58 | } 59 | internal override void WriteValue(BinaryFormatter formatter, Int64 value) 60 | { 61 | formatter.WriteInt64(value); 62 | } 63 | } 64 | 65 | class NullableInt64Serializer : ValueSerializer 66 | { 67 | internal override Int64? ReadValue(BinaryFormatter formatter) 68 | { 69 | return formatter.ReadNullableInt64(); 70 | } 71 | internal override void WriteValue(BinaryFormatter formatter, Int64? value) 72 | { 73 | formatter.WriteNullableInt64(value); 74 | } 75 | } 76 | 77 | } 78 | 79 | -------------------------------------------------------------------------------- /MarcelloDB/Serialization/ValueSerializers/FloatSerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MarcelloDB.Serialization.ValueSerializers 4 | { 5 | class DecimalSerializer : ValueSerializer 6 | { 7 | internal override Decimal ReadValue(BinaryFormatter formatter) 8 | { 9 | return formatter.ReadDecimal(); 10 | } 11 | internal override void WriteValue(BinaryFormatter formatter, Decimal value) 12 | { 13 | formatter.WriteDecimal(value); 14 | } 15 | } 16 | 17 | class NullableDecimalSerializer : ValueSerializer 18 | { 19 | internal override Decimal? ReadValue(BinaryFormatter formatter) 20 | { 21 | return formatter.ReadNullableDecimal(); 22 | } 23 | internal override void WriteValue(BinaryFormatter formatter, Decimal? value) 24 | { 25 | formatter.WriteNullableDecimal(value); 26 | } 27 | } 28 | 29 | class SingleSerializer : ValueSerializer 30 | { 31 | internal override Single ReadValue(BinaryFormatter formatter) 32 | { 33 | return formatter.ReadSingle(); 34 | } 35 | internal override void WriteValue(BinaryFormatter formatter, Single value) 36 | { 37 | formatter.WriteSingle(value); 38 | } 39 | } 40 | 41 | class NullableSingleSerializer : ValueSerializer 42 | { 43 | internal override Single? ReadValue(BinaryFormatter formatter) 44 | { 45 | return formatter.ReadNullableSingle(); 46 | } 47 | internal override void WriteValue(BinaryFormatter formatter, Single? value) 48 | { 49 | formatter.WriteNullableSingle(value); 50 | } 51 | } 52 | 53 | class DoubleSerializer : ValueSerializer 54 | { 55 | internal override Double ReadValue(BinaryFormatter formatter) 56 | { 57 | return formatter.ReadDouble(); 58 | } 59 | internal override void WriteValue(BinaryFormatter formatter, Double value) 60 | { 61 | formatter.WriteDouble(value); 62 | } 63 | } 64 | 65 | class NullableDoubleSerializer : ValueSerializer 66 | { 67 | internal override Double? ReadValue(BinaryFormatter formatter) 68 | { 69 | return formatter.ReadNullableDouble(); 70 | } 71 | internal override void WriteValue(BinaryFormatter formatter, Double? value) 72 | { 73 | formatter.WriteNullableDouble(value); 74 | } 75 | } 76 | 77 | } 78 | 79 | -------------------------------------------------------------------------------- /MarcelloDB.Test/Index/IndexTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using MarcelloDB.Index; 4 | using System.Collections.Generic; 5 | 6 | namespace MarcelloDB.Test.Index 7 | { 8 | [TestFixture] 9 | public class IndexTest 10 | { 11 | MockBTree _mockTree; 12 | MockBTreeDataProvider _mockProvider; 13 | 14 | RecordIndex _index; 15 | Session _session; 16 | 17 | [SetUp] 18 | public void Initialize() 19 | { 20 | _session = new Session(new TestPlatform(), ""); 21 | _mockTree = new MockBTree(); 22 | _mockProvider = new MockBTreeDataProvider(); 23 | _index = new RecordIndex(_session, _mockTree, _mockProvider); 24 | } 25 | 26 | [Test] 27 | public void Search_Returns_Zero_When_Index_Empty() 28 | { 29 | Assert.AreEqual(0, _index.Search(123)); 30 | } 31 | 32 | [Test] 33 | public void Search_Returns_Zero_When_Key_Not_Found() 34 | { 35 | _mockTree.Insert(1, 2); 36 | Assert.AreEqual(0, _index.Search(123)); 37 | } 38 | 39 | [Test] 40 | public void Search_Returns_Record_Address_When_Key_Found() 41 | { 42 | _mockTree.Insert(1, 2); 43 | Assert.AreEqual(2, _index.Search(1)); 44 | } 45 | 46 | [Test] 47 | public void Register_Inserts_When_Key_Not_Present() 48 | { 49 | _index.Register(1, 2); 50 | Assert.AreEqual(2, _mockTree.Inserted[1]); 51 | } 52 | 53 | [Test] 54 | public void Register_Inserts_New_Value_On_Update() 55 | { 56 | _mockTree.Insert(1, 2); 57 | _index.Register(1, 3); 58 | Assert.AreEqual(3, _mockTree.Inserted[1]); 59 | Assert.AreEqual("Insert", _mockTree.LastAction); 60 | } 61 | 62 | [Test] 63 | public void Register_Flushes_DataProvider() 64 | { 65 | _index.Register(1, 123); 66 | Assert.IsTrue(_mockProvider.WasFlushed); 67 | } 68 | 69 | [Test] 70 | public void UnRegister_Deletes_Original_Value() 71 | { 72 | _mockTree.Insert(1, 2); 73 | _index.UnRegister(1); 74 | Assert.AreEqual(1, _mockTree.Deleted[0]); 75 | } 76 | 77 | [Test] 78 | public void UnRegister_Flushes_DataProvider() 79 | { 80 | _index.UnRegister(1); 81 | Assert.IsTrue(_mockProvider.WasFlushed); 82 | } 83 | } 84 | } 85 | 86 | -------------------------------------------------------------------------------- /MarcelloDB/Collections/IndexEntryEnumerator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using MarcelloDB.Collections; 4 | using MarcelloDB.Index; 5 | using MarcelloDB.Index.BTree; 6 | using System.Collections; 7 | 8 | namespace MarcelloDB 9 | { 10 | internal class IndexEntryEnumerator : SessionBoundObject, IEnumerable> 11 | { 12 | Collection Collection { get; set; } 13 | 14 | RecordIndex Index { get; set; } 15 | 16 | BTreeWalkerRange Range { get; set; } 17 | 18 | bool HasRange{ get { return this.Range != null; } } 19 | 20 | bool IsDescending { get; set; } 21 | 22 | public IndexEntryEnumerator( 23 | Collection collection, 24 | Session session, 25 | RecordIndex index, 26 | bool isDescending = false 27 | ) : base(session) 28 | { 29 | this.Collection = collection; 30 | this.Index = index; 31 | this.IsDescending = isDescending; 32 | } 33 | 34 | public void SetRange(BTreeWalkerRange range) 35 | { 36 | this.Range = range; 37 | } 38 | 39 | #region IEnumerable implementation 40 | IEnumerator> IEnumerable>.GetEnumerator() 41 | { 42 | //execute empty transaction, to apply any comitted but unapplied transactions 43 | this.Session.Transaction (() => { }); 44 | lock(Session.SyncLock){ 45 | try{ 46 | this.Collection.BlockModification = true; 47 | var walker = this.Index.GetWalker(); 48 | 49 | if(this.IsDescending) 50 | { 51 | walker.Reverse(); 52 | } 53 | 54 | if(this.HasRange) 55 | { 56 | walker.SetRange(this.Range); 57 | } 58 | 59 | var node = walker.Next(); 60 | while (node != null) 61 | { 62 | yield return node; 63 | node = walker.Next(); 64 | } 65 | } 66 | finally 67 | { 68 | this.Collection.BlockModification = false; 69 | } 70 | } 71 | } 72 | #endregion 73 | #region IEnumerable implementation 74 | IEnumerator IEnumerable.GetEnumerator() 75 | { 76 | return ((IEnumerable)this).GetEnumerator(); 77 | } 78 | #endregion 79 | } 80 | } 81 | 82 | -------------------------------------------------------------------------------- /MarcelloDB.Test/AllocationStrategies/PredictiveBTreeNodeAllocationStrategyTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using MarcelloDB.Index; 4 | using MarcelloDB.AllocationStrategies; 5 | 6 | namespace MarcelloDB.Test.AllocationStrategies 7 | { 8 | [TestFixture] 9 | public class PredictiveBTreeNodeAllocationStrategyTest 10 | { 11 | [Test] 12 | public void It_Allocates_2000_Percent_For_A_10_Percent_Filled_Non_ValueType_Leaf_Node() 13 | { 14 | var node = new Node(5); 15 | var bytes = new byte[100]; 16 | node.EntryList.Add(new Entry()); 17 | 18 | var strategy = new PredictiveBTreeNodeAllocationStrategy(node); 19 | var calculated = strategy.CalculateSize(bytes.Length); 20 | 21 | var diff = Math.Abs(bytes.Length * 20 - calculated); 22 | Assert.IsTrue(diff <= bytes.Length * 2); // max 10% off 23 | } 24 | 25 | [Test] 26 | public void It_Allocates_2000_Percent_For_A_10_Percent_Filled_Non_ValueType_Non_Leaf_Node() 27 | { 28 | var node = new Node(5); 29 | var bytes = new byte[100]; 30 | node.EntryList.Add(new Entry()); 31 | node.ChildrenAddresses.Add(1); 32 | 33 | var strategy = new PredictiveBTreeNodeAllocationStrategy(node); 34 | var calculated = strategy.CalculateSize(bytes.Length); 35 | 36 | var diff = Math.Abs(bytes.Length * 20 - calculated); 37 | Assert.IsTrue(diff <= bytes.Length * 2); // max 10% off 38 | } 39 | 40 | [Test] 41 | public void It_Allocates_1000_Percent_For_A_10_Percent_Filled_ValueType_Non_Leaf_Node() 42 | { 43 | var node = new Node(5); 44 | var bytes = new byte[100]; 45 | node.EntryList.Add(new Entry()); 46 | node.ChildrenAddresses.Add(1); 47 | 48 | var strategy = new PredictiveBTreeNodeAllocationStrategy(node); 49 | var calculated = strategy.CalculateSize(bytes.Length); 50 | 51 | var diff = Math.Abs(bytes.Length * 10 - calculated); 52 | Assert.IsTrue(diff <= bytes.Length * 2); // max 10% off 53 | } 54 | 55 | [Test] 56 | public void It_Allocates_Exact_Size_For_Empty_Nodes() 57 | { 58 | var node = new Node(5); 59 | var bytes = new byte[100]; 60 | 61 | var strategy = new PredictiveBTreeNodeAllocationStrategy(node); 62 | var calculated = strategy.CalculateSize(bytes.Length); 63 | Assert.AreEqual(bytes.Length, calculated); 64 | 65 | } 66 | } 67 | } 68 | 69 | -------------------------------------------------------------------------------- /MarcelloDB.Test/Index/IndexDefinitionTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using MarcelloDB.Test.Classes; 4 | using MarcelloDB.Index; 5 | using System.Collections.Generic; 6 | using MarcelloDB.Collections; 7 | using System.Linq; 8 | using MarcelloDB.Records; 9 | 10 | namespace MarcelloDB.Test.Index 11 | { 12 | class TestDefinition : IndexDefinition
13 | { 14 | public IndexedValue Name{ get; set; } 15 | 16 | public IndexedValue CustomDescription 17 | { 18 | get 19 | { 20 | return IndexedValue((Article article)=>{ 21 | return "Custom" + article.Description; 22 | }); 23 | } 24 | } 25 | } 26 | 27 | [TestFixture] 28 | public class IndexDefinitionTest 29 | { 30 | TestDefinition _definition; 31 | 32 | [SetUp] 33 | public void Initialize() 34 | { 35 | _definition = new TestDefinition(); 36 | _definition.Initialize(); 37 | } 38 | 39 | [Test] 40 | public void IndexedValues_Contains_IndexedValue_For_Empty_Property() 41 | { 42 | var indexedValues = _definition.IndexedValues; 43 | Assert.NotNull(indexedValues.FirstOrDefault(v => v.PropertyName == "Name")); 44 | } 45 | 46 | [Test] 47 | public void IndexedValues_Contains_Indexed_Value_For_Implemented_Property() 48 | { 49 | var indexedValues = _definition.IndexedValues; 50 | Assert.NotNull(indexedValues.FirstOrDefault(v => v.PropertyName == "CustomDescription")); 51 | } 52 | 53 | [Test] 54 | public void IndexedField_GetKey_Returns_Correct_Key_For_Empty_Property() 55 | { 56 | var indexedValues = _definition.IndexedValues; 57 | var indexKey = (ValueWithAddressIndexKey) indexedValues.FirstOrDefault(v => v.PropertyName == "Name") 58 | .GetKeys(Article.BarbieDoll, 123).First(); 59 | Assert.AreEqual(Article.BarbieDoll.Name, indexKey.V); 60 | Assert.AreEqual(123, indexKey.A); 61 | } 62 | 63 | [Test] 64 | public void IndexedField_GetKey_Returns_Correct_Key_For_Custom_Property() 65 | { 66 | var indexedValues = _definition.IndexedValues; 67 | var indexKey = (ValueWithAddressIndexKey) indexedValues.FirstOrDefault(v => v.PropertyName == "CustomDescription") 68 | .GetKeys(Article.BarbieDoll, 123).First(); 69 | Assert.AreEqual("Custom" + Article.BarbieDoll.Description, indexKey.V); 70 | Assert.AreEqual(123, indexKey.A); 71 | } 72 | } 73 | } 74 | 75 | -------------------------------------------------------------------------------- /MarcelloDB.Test/Classes/Article.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MarcelloDB.Test.Classes 4 | { 5 | public class Article 6 | { 7 | public Article () 8 | { 9 | this.Reference = Guid.NewGuid().ToString(); 10 | } 11 | 12 | public string Name{ get; set;} 13 | 14 | public string ShortName { get; set; } 15 | 16 | public string Description { get; set; } 17 | 18 | public int ID { get; set;} 19 | 20 | public string Code { get; set; } 21 | 22 | public string Category { get; set; } 23 | 24 | public string Reference { get; set; } 25 | 26 | public static Article ToiletPaper 27 | { 28 | get 29 | { 30 | return new Article(){ 31 | ID = 1, 32 | Code = "001", 33 | Name = "Toilet Paper", 34 | ShortName = "Toilet P.", 35 | Description = "The finest paper money can buy", 36 | Category = "Hygiene", 37 | Reference = "TLT001" 38 | }; 39 | } 40 | } 41 | 42 | public static Article SpinalTapDvd 43 | { 44 | get 45 | { 46 | return new Article(){ 47 | ID = 2, 48 | Code = "002", 49 | Name = "DVD: This is Spinal Tap", 50 | ShortName = "T.i. Spinal Tap", 51 | Description = "Best dvd ever", 52 | Category = "Entertainment", 53 | Reference = "DVD002" 54 | }; 55 | } 56 | } 57 | 58 | public static Article BarbieDoll 59 | { 60 | get 61 | { 62 | return new Article(){ 63 | ID = 3, 64 | Code = "003", 65 | Name = "Barbie Doll", 66 | ShortName = "Barb d", 67 | Description = "Some doll", 68 | Category = "Toys", 69 | Reference = "TOY003" 70 | }; 71 | } 72 | } 73 | } 74 | 75 | public class Food : Article 76 | { 77 | public DateTime Expires {get;set;} 78 | 79 | 80 | public static Food Bread 81 | { 82 | get 83 | { 84 | return new Food{ 85 | ID = 4, 86 | Code = "004", 87 | Expires = DateTime.Now.AddDays(2), 88 | Name = "Bread", 89 | ShortName = "Bread", 90 | Description = "White Bread", 91 | Category = "Bread" 92 | }; 93 | } 94 | } 95 | } 96 | } 97 | 98 | -------------------------------------------------------------------------------- /MarcelloDB/Serialization/BufferWriter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MarcelloDB.Serialization 4 | { 5 | internal class BufferWriter 6 | { 7 | internal byte[] Buffer { get; set; } 8 | 9 | internal bool IsLittleEndian { get; set; } 10 | 11 | internal int Position { get; set; } 12 | 13 | internal BufferWriter(byte[] buffer) 14 | { 15 | Initialize(buffer, BitConverter.IsLittleEndian); 16 | } 17 | 18 | internal BufferWriter(byte[] buffer, bool isLittleEndian) 19 | { 20 | Initialize(buffer, isLittleEndian); 21 | } 22 | 23 | internal BufferWriter WriteByte(byte value) 24 | { 25 | WriteBytes(new byte[]{ value }); 26 | return this; 27 | } 28 | 29 | public BufferWriter WriteInt16(Int16 value) 30 | { 31 | WriteBytes(LittleEndian(BitConverter.GetBytes(value))); 32 | return this; 33 | } 34 | 35 | internal BufferWriter WriteInt32(Int32 value) 36 | { 37 | WriteBytes(LittleEndian(BitConverter.GetBytes(value))); 38 | return this; 39 | } 40 | 41 | internal BufferWriter WriteInt64(Int64 value) 42 | { 43 | WriteBytes(LittleEndian(BitConverter.GetBytes(value))); 44 | return this; 45 | } 46 | 47 | internal BufferWriter WriteBytes(byte[] bytes) 48 | { 49 | if (this.Position + bytes.Length > Buffer.Length) 50 | { 51 | //Double buffer 52 | var doubleSize = (this.Position + bytes.Length) * 2; 53 | var newBuffer = new byte[(int)doubleSize]; 54 | this.Buffer.CopyTo(newBuffer, 0); 55 | this.Buffer = newBuffer; 56 | } 57 | bytes.CopyTo(Buffer, this.Position); 58 | this.Position += bytes.Length; 59 | return this; 60 | } 61 | 62 | internal byte[] GetTrimmedBuffer() 63 | { 64 | if (this.Buffer.Length == this.Position) 65 | { 66 | return this.Buffer; 67 | } 68 | else 69 | { 70 | var trimmed = new byte[this.Position]; 71 | Array.Copy(this.Buffer, trimmed, this.Position); 72 | return trimmed; 73 | } 74 | } 75 | 76 | void Initialize(byte[] buffer, bool isLittleEndian) 77 | { 78 | this.Buffer = buffer; 79 | this.Position = 0; 80 | this.IsLittleEndian = isLittleEndian; 81 | } 82 | 83 | byte[] LittleEndian(byte[] bytes) 84 | { 85 | if (!this.IsLittleEndian) 86 | { 87 | Array.Reverse(bytes); 88 | } 89 | return bytes; 90 | } 91 | } 92 | } 93 | 94 | 95 | -------------------------------------------------------------------------------- /MarcelloDB/Collections/Scopes/BaseScope.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using MarcelloDB.Records; 5 | using MarcelloDB.Collections; 6 | using System.Collections; 7 | using MarcelloDB.Index; 8 | 9 | namespace MarcelloDB.Collections.Scopes 10 | { 11 | 12 | public abstract class BaseScope : IEnumerable 13 | { 14 | ObjectComparer Comparer { get; set; } 15 | 16 | public BaseScope() 17 | { 18 | this.Comparer = new ObjectComparer(); 19 | } 20 | 21 | abstract internal CollectionEnumerator> BuildEnumerator(bool descending); 22 | 23 | public Descending Descending 24 | { 25 | get 26 | { 27 | return new Descending(this); 28 | } 29 | } 30 | 31 | public IEnumerable Keys 32 | { 33 | get 34 | { 35 | var keyEnumerator = (IEnumerable>) BuildEnumerator(false) 36 | .GetKeyEnumerator(); 37 | 38 | IComparable previousKeyValue = null; 39 | 40 | bool first = true; 41 | 42 | foreach (var key in keyEnumerator) 43 | { 44 | var currentKeyValue = (IComparable)key.V; 45 | if (first) 46 | { 47 | yield return (TAttribute)currentKeyValue; 48 | } 49 | else 50 | { 51 | if (previousKeyValue == null) 52 | { 53 | if (currentKeyValue != null) 54 | { 55 | yield return (TAttribute)currentKeyValue; 56 | } 57 | } 58 | else if (this.Comparer.Compare(previousKeyValue, currentKeyValue) != 0) 59 | { 60 | yield return (TAttribute)currentKeyValue; 61 | } 62 | } 63 | 64 | first = false; 65 | previousKeyValue = currentKeyValue; 66 | } 67 | } 68 | } 69 | 70 | #region IEnumerable implementation 71 | 72 | IEnumerator IEnumerable.GetEnumerator() 73 | { 74 | //default to ascending enumeration 75 | return ((IEnumerable)BuildEnumerator(false)).GetEnumerator(); 76 | } 77 | 78 | #endregion 79 | 80 | #region IEnumerable implementation 81 | 82 | IEnumerator IEnumerable.GetEnumerator() 83 | { 84 | return ((IEnumerable)this).GetEnumerator(); 85 | } 86 | 87 | #endregion 88 | } 89 | } 90 | 91 | -------------------------------------------------------------------------------- /MarcelloDB.Test/Regression/Issue69_OutOfMemoryOnEmptyCollection.cs: -------------------------------------------------------------------------------- 1 | using MarcelloDB.Collections; 2 | using NUnit.Framework; 3 | using System; 4 | using System.IO; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using MarcelloDB.netfx; 10 | 11 | 12 | //https://github.com/markmeeus/MarcelloDB/issues/69 13 | namespace MarcelloDB.Test.Regression.Issue69 14 | { 15 | public class Product 16 | { 17 | public Guid Id { get; set; } 18 | public string Payload { get; set; } 19 | } 20 | 21 | public class Customer 22 | { 23 | public int Id { get; set; } 24 | public string Payload { get; set; } 25 | } 26 | 27 | [TestFixture] 28 | public class Issue69_OutOfMemoryOnEmptyCollection 29 | { 30 | [Test] 31 | public void Should_Not_Go_Corrupt_After_Enumerating_Empty_Collections () 32 | { 33 | var platform = new TestPlatform (); 34 | Session session = new Session (platform, "/"); 35 | var collectionFile = session ["articles"]; 36 | var objects = collectionFile.Collection ("objects", p => p.ToString()); 37 | 38 | //insert something so that the root exists: 39 | objects.Persist (1); 40 | 41 | var products = collectionFile.Collection ("products", p => p.Id); 42 | var customers = collectionFile.Collection ("customers", p => p.Id); 43 | 44 | // Creating an enumerator caused a root node to be inserted, out of any transaction 45 | // the recordmanager was aware of. 46 | // The second enumerator actually runs an empty transaction to make sure any data is flushed, 47 | // the recordmanager's head however was not updated in the data file. 48 | products.All.ToList (); 49 | customers.All.ToList (); 50 | 51 | //the product collection now has a root node for it's id index at 'head' location 52 | //the customer only has one in the journal 53 | //Without a transaction in that specific collection file, the head itself was not updated 54 | session.Dispose (); 55 | session = new Session (platform, "/"); 56 | collectionFile = session ["articles"]; 57 | 58 | objects = collectionFile.Collection ("objects", p => p.ToString ()); 59 | products = collectionFile.Collection ("products", p => p.Id); 60 | customers = collectionFile.Collection ("customers", p => p.Id); 61 | 62 | //add some data after head, it will overwrite the collection's index root nodes 63 | objects.Persist (2); 64 | objects.Persist (3); 65 | objects.Persist (4); 66 | 67 | //inserting something at the head caused the next enumeration to fail 68 | products.All.ToList (); 69 | customers.All.ToList (); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /MarcelloDB.Test/Serialization/EmptyRecordIndexNodeSerializerTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using MarcelloDB.Records; 4 | using MarcelloDB.Index; 5 | using MarcelloDB.Serialization; 6 | 7 | namespace MarcelloDB.Test.Serialization 8 | { 9 | [TestFixture] 10 | public class EmptyRecordIndexNodeSerializerTest 11 | { 12 | [SetUp] 13 | public void Initialize() 14 | { 15 | } 16 | 17 | [Test] 18 | public void Serializes_OK() 19 | { 20 | var node = new Node(12); 21 | var serializer = new EmptyRecordIndexNodeSerializer(); 22 | node.ChildrenAddresses.Add(123); 23 | node.ChildrenAddresses.Add(456); 24 | node.ChildrenAddresses.Add(678); 25 | node.EntryList.Add(new Entry{ 26 | Key = new EmptyRecordIndexKey{S = 123, A = 456 }, 27 | Pointer = 678 28 | }); 29 | node.EntryList.Add(new Entry{ 30 | Key = new EmptyRecordIndexKey{S = 321, A = 654 }, 31 | Pointer = 876 32 | }); 33 | var deserialized = serializer.Deserialize( 34 | serializer.Serialize(node) 35 | ); 36 | 37 | Assert.AreEqual(node.Degree, deserialized.Degree); 38 | Assert.AreEqual(node.ChildrenAddresses.Addresses, deserialized.ChildrenAddresses.Addresses); 39 | Assert.AreEqual(node.EntryList.Count, deserialized.EntryList.Count); 40 | 41 | Assert.AreEqual(node.EntryList[0].Pointer, deserialized.EntryList[0].Pointer); 42 | Assert.AreEqual(node.EntryList[0].Key, deserialized.EntryList[0].Key); 43 | Assert.AreEqual(node.EntryList[1].Pointer, deserialized.EntryList[1].Pointer); 44 | Assert.AreEqual(node.EntryList[1].Key, deserialized.EntryList[1].Key); 45 | 46 | } 47 | 48 | [Test] 49 | public void Empty_And_Full_Nodes_Have_Same_Byte_Size() 50 | { 51 | var node = new Node(12); 52 | var serializer = new EmptyRecordIndexNodeSerializer(); 53 | var emptyBytes = serializer.Serialize(node); 54 | for(int i = 1; i <= Node.MaxEntriesForDegree(12); i++) //max nr of entries for degree 12 55 | { 56 | node.EntryList.Add( 57 | new Entry{ 58 | Key = new EmptyRecordIndexKey{S = 1, A = 1}, 59 | Pointer = i 60 | } 61 | ); 62 | } 63 | for(int i = 1; i <= Node.MaxChildrenForDegree(12); i++) //max nr of childres for degree 12 64 | { 65 | node.ChildrenAddresses.Add(i); 66 | } 67 | 68 | var fullBytes = serializer.Serialize(node); 69 | Assert.AreEqual(emptyBytes.Length, fullBytes.Length); 70 | } 71 | 72 | } 73 | } 74 | 75 | -------------------------------------------------------------------------------- /MarcelloDB.Test/Regression/Issue67_RootNotSavedAfterRecycle.cs: -------------------------------------------------------------------------------- 1 | using MarcelloDB.Collections; 2 | using NUnit.Framework; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | //https://github.com/markmeeus/MarcelloDB/issues/67 10 | namespace MarcelloDB.Test.Regression.Issue67 11 | { 12 | public class Product 13 | { 14 | public int Id { get; set; } 15 | public byte [] Payload { get; set; } 16 | } 17 | 18 | [TestFixture] 19 | public class Issue67_RootNotSavedAfterRecycle 20 | { 21 | 22 | [SetUp] 23 | public void Setup () 24 | { 25 | } 26 | 27 | [Test] 28 | public void Persist_With_Id_Property () 29 | { 30 | var platform = new TestPlatform (); 31 | Session session = new Session (platform, "/"); 32 | var collectionFile = session ["articles"]; 33 | var products = collectionFile.Collection ("products", p => p.Id); 34 | 35 | Product product; 36 | for (int i = 0; i < MarcelloDB.Index.RecordIndex.BTREE_DEGREE * 3; i ++){ 37 | product = new Product (); 38 | product.Id = i; 39 | products.Persist (product); 40 | } 41 | 42 | // Destroy will cause the record to be recycled. 43 | // a new node will be added to the empty record index, cause head to move 44 | // Issue67 caused this head not be updated in the transaction 45 | session.Transaction (() => { 46 | for (int i = 0; i < MarcelloDB.Index.RecordIndex.BTREE_DEGREE * 3; i++) { 47 | products.Destroy (i); 48 | } 49 | }); 50 | 51 | // close the session 52 | session.Dispose(); 53 | 54 | // create a new session 55 | session = new Session (platform, "/"); 56 | collectionFile = session ["articles"]; 57 | products = collectionFile.Collection ("products", p => p.Id); 58 | 59 | // Add large products 60 | // These will not reuse the previously deleted records, cause an overwrite 61 | // of the empty record index nodes form previous transaction 62 | for (int i = 0; i < MarcelloDB.Index.RecordIndex.BTREE_DEGREE * 3; i++) { 63 | product = new Product (); 64 | product.Id = i; 65 | product.Payload = new byte [1024]; 66 | products.Persist (product); 67 | } 68 | 69 | // Destroy all these objects will try to deplete the entire empty record index. 70 | // Which is currupted by the previous destroying transaction 71 | // This causes unpredictable errors because the data is corrupted 72 | for (int i = 0; i < MarcelloDB.Index.RecordIndex.BTREE_DEGREE * 3; i++) { 73 | products.Destroy (i); 74 | } 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /MarcelloDB/Serialization/TransactionJournalSerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using MarcelloDB.Transactions; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace MarcelloDB.Serialization 7 | { 8 | internal class TransactionJournalSerializer : IObjectSerializer 9 | { 10 | const int FORMAT_VERSION = 1; 11 | 12 | public TransactionJournalSerializer() 13 | { 14 | } 15 | 16 | #region IObjectSerializer implementation 17 | 18 | public byte[] Serialize(TransactionJournal journal) 19 | { 20 | var expectedSize = sizeof(byte) + sizeof(Int32) + journal.Entries.Select(entry => 21 | { 22 | return sizeof(Int64) + 23 | sizeof(Int32) + 24 | entry.Data.Length + 25 | sizeof(Int64) + 26 | sizeof(Int32) + Encoding.UTF8.GetByteCount(entry.StreamName); 27 | }).Sum(); 28 | 29 | var writer = new BufferWriter(new byte[expectedSize]); 30 | 31 | writer.WriteByte(FORMAT_VERSION); 32 | writer.WriteInt32(journal.Entries.Count); 33 | 34 | foreach (var entry in journal.Entries) 35 | { 36 | WriteEntry(writer, entry); 37 | } 38 | return writer.GetTrimmedBuffer(); 39 | } 40 | 41 | public TransactionJournal Deserialize(byte[] bytes) 42 | { 43 | var reader = new BufferReader(bytes); 44 | reader.ReadByte(); //FORMAT_VERSION 45 | 46 | var journal = new TransactionJournal(); 47 | var entryCount = reader.ReadInt32(); 48 | 49 | for (int i = 0; i < entryCount; i++) 50 | { 51 | journal.Entries.Add(ReadEntry(reader)); 52 | } 53 | return journal; 54 | } 55 | 56 | #endregion 57 | 58 | void WriteEntry(BufferWriter writer, JournalEntry entry) 59 | { 60 | writer.WriteInt64(entry.Address); 61 | writer.WriteInt32(entry.Data.Length); 62 | writer.WriteBytes(entry.Data); 63 | writer.WriteInt64(entry.Stamp); 64 | var streamNameBytes = Encoding.UTF8.GetBytes(entry.StreamName); 65 | writer.WriteInt32(streamNameBytes.Length); 66 | writer.WriteBytes(streamNameBytes); 67 | } 68 | 69 | JournalEntry ReadEntry(BufferReader reader) 70 | { 71 | var journalEntry = new JournalEntry(); 72 | journalEntry.Address = reader.ReadInt64(); 73 | var dataLength = reader.ReadInt32(); 74 | journalEntry.Data = reader.ReadBytes(dataLength); 75 | journalEntry.Stamp = reader.ReadInt64(); 76 | var streamNameLength = reader.ReadInt32(); 77 | var streamNameBytes = reader.ReadBytes(streamNameLength); 78 | 79 | journalEntry.StreamName = Encoding.UTF8.GetString(streamNameBytes, 0, streamNameBytes.Length); 80 | return journalEntry; 81 | } 82 | } 83 | } 84 | 85 | -------------------------------------------------------------------------------- /couscous.yml: -------------------------------------------------------------------------------- 1 | template: 2 | # Name of the directory containing the website template (default is "website") 3 | directory: docs/template 4 | 5 | # Name of the index file (default is "README.md") 6 | index: README.md 7 | 8 | # List of directories to include in the processing (by default it's empty, so all markdown files are parsed) 9 | # Paths are relative to the optional source path given when generating the website, repository root by default 10 | include: 11 | - docs 12 | 13 | # List of directories to exclude from the processing (default contains "vendor" and "website") 14 | # Paths are relative to the optional include paths given when generating the website, repository root by default 15 | exclude: 16 | 17 | 18 | scripts: 19 | # Scripts to execute before generating the website 20 | before: 21 | # - cp bin/couscous.phar website/ 22 | # Scripts to execute after generating the website 23 | after: 24 | # - rm website/couscous.phar 25 | 26 | # Set this variable to use a Custom Domain 27 | # The content of this variable will be directly inserted into the CNAME file 28 | #cname: docs.yourdomain.com 29 | 30 | # Set the target branch in which to deploy the generated website 31 | branch: gh-pages 32 | github: 33 | user: markmeeus 34 | repo: marcellodb 35 | # Any variable you put in this file is also available in the Twig layouts: 36 | title: MarcelloDB 37 | logo: http://markmeeus.github.io/MarcelloDB/images/logo/logo_blue.svg 38 | subTitle: NoSQL Data Storage for Xamarin and Windows Apps 39 | # Base URL of the published website (no "/" at the end!) 40 | # You are advised to set and use this variable to write your links in the HTML layouts 41 | baseUrl: http://markmeeus.github.io/MarcelloDB 42 | 43 | menu: 44 | sections: 45 | main: 46 | name: Main documentation 47 | items: 48 | home: 49 | text: Home 50 | relativeUrl: 51 | quickstart: 52 | text: Quickstart Guide 53 | relativeUrl: quickstart.html 54 | sessions: 55 | text: Sessions, CollectionFiles and Collections 56 | relativeUrl: sessions.html 57 | persisting: 58 | text: Persist and Destroy 59 | relativeUrl: persisting.html 60 | reading: 61 | text: Find and Enumerate 62 | relativeUrl: reading.html 63 | indexes: 64 | text: Indexes 65 | relativeUrl: indexes.html 66 | transactions: 67 | text: Transactions 68 | relativeUrl: transactions.html 69 | encryption: 70 | text: Encryption 71 | relativeUrl: encryption.html 72 | # contributing: 73 | # text: Contributing 74 | # relativeUrl: contributing.html 75 | upgrade04: 76 | text: Upgrading to 0.4 77 | relativeUrl: upgrade04.html 78 | upgrade05: 79 | text: Upgrading to 0.5 80 | relativeUrl: upgrade05.html 81 | roadmap: 82 | text: Roadmap 83 | relativeUrl: roadmap.html 84 | longterm: 85 | text: After 1.0 86 | relativeUrl: longterm.html 87 | -------------------------------------------------------------------------------- /MarcelloDB.W81/MarcelloDB.W81.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 12.0 6 | Debug 7 | AnyCPU 8 | {4CA45E6E-2B22-49D2-9DFA-A030D5850AAE} 9 | Library 10 | Properties 11 | MarcelloDB.W81 12 | MarcelloDB.W81 13 | en-US 14 | 512 15 | {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 16 | Profile32 17 | v4.6 18 | 19 | 20 | true 21 | full 22 | false 23 | bin\Debug\ 24 | DEBUG;TRACE 25 | prompt 26 | 4 27 | 28 | 29 | pdbonly 30 | true 31 | bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | 36 | 37 | 38 | 39 | {4172a119-2ae8-41d9-999e-9a2468267b28} 40 | MarcelloDB 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 60 | --------------------------------------------------------------------------------