├── .gitignore ├── Coverage.testsettings ├── Examples ├── AnalyzeTicks │ ├── AnalyzeTicks.csproj │ └── Program.cs ├── Benchmarks │ ├── Benchmarks.csproj │ ├── ComputerSpecs.cs │ ├── Operations.cs │ ├── Program.cs │ ├── Runner.cs │ ├── Utils.cs │ └── results.htm ├── CreateSessions │ ├── CreateSessions.csproj │ └── Program.cs ├── CreateTicks │ ├── CreateTicks.csproj │ └── Program.cs ├── GetSnapshot │ ├── GetSnapshot.csproj │ └── Program.cs ├── ItemTypes │ ├── ItemTypes.cs │ └── ItemTypes.csproj └── Sum │ ├── Program.cs │ └── Sum.csproj ├── LICENSE ├── Lib ├── Moq.dll ├── Moq.xml ├── SharpTestsEx.dll └── SharpTestsEx.xml ├── Normal.testsettings ├── README.md ├── TeaFiles.Net.sln ├── TeaFiles.Test ├── Access │ ├── ItemCollectionTest.cs │ ├── ManagedMemoryMappingTest.cs │ ├── RawMemoryMappingTest.cs │ └── SafeBufferTest.cs ├── Base │ ├── EventTest.cs │ ├── NameValueTest.cs │ ├── TimeScaleTest.cs │ └── TimeTest.cs ├── Description │ ├── DescriptionTest.cs │ ├── FieldTest.cs │ ├── ItemDescriptionDetailsTest.cs │ ├── ItemDescriptionTest.cs │ └── TeaTypeTest.cs ├── ExtensionsTest.cs ├── Header │ ├── HeaderManagerTest.cs │ └── Sections │ │ ├── ContentSectionFormatterTest.cs │ │ ├── ItemSectionFormatterTest.cs │ │ ├── NameValueSectionFormatterTest.cs │ │ └── TimeSectionFormatterTest.cs ├── IO │ ├── FileIOTest.cs │ ├── FormattedReaderTest.cs │ └── FormattedWriterTest.cs ├── ItemsTTest.cs ├── Layout │ ├── ByteSearcherTest.cs │ └── LayoutAnalyzerTest.cs ├── Properties │ └── AssemblyInfo.cs ├── SampleItems │ └── ItemTypes.cs ├── SnapshotTest.cs ├── TeaFileCoreTest.cs ├── TeaFileTTest.cs ├── TeaFileTest.cs ├── TeaFiles.Test.csproj ├── TestStream.cs ├── TestUtils.cs └── packages.config ├── TeaFiles ├── Access │ ├── Extensions.cs │ ├── ManagedMemoryMapping.cs │ ├── RawMemoryMapping.cs │ ├── Typed │ │ ├── ItemsCollectionT.cs │ │ └── SafeBufferT.cs │ └── UnTyped │ │ ├── IItemReader.cs │ │ ├── Item.cs │ │ └── ItemCollection.cs ├── Base │ ├── Event.cs │ ├── NameValue.cs │ ├── Time.cs │ └── TimeScale.cs ├── Description │ ├── DescriptionSource.cs │ ├── Field.cs │ ├── FieldType.cs │ ├── ItemDescription.cs │ ├── ItemDescriptionElements.cs │ ├── TeaFileDescription.cs │ └── TimeFormat.cs ├── Exceptions.cs ├── Extensions.cs ├── GlobalSuppressions.cs ├── Header │ ├── HeaderManager.cs │ ├── ISectionFormatter.cs │ ├── ReadContext.cs │ ├── Sections │ │ ├── ContentSectionFormatter.cs │ │ ├── ItemSectionFormatter.cs │ │ ├── NameValueSectionFormatter.cs │ │ └── TimeSectionFormatter.cs │ └── WriteContext.cs ├── IO │ ├── FileIO.cs │ ├── FormattedReader.cs │ ├── FormattedWriter.cs │ └── Interfaces.cs ├── Layout │ ├── AnalyzedField.cs │ ├── ByteSearcher.cs │ ├── FieldPath.cs │ └── LayoutAnalyzer.cs ├── License.txt ├── TeaFile.cs ├── TeaFileCore.cs ├── TeaFileT.cs ├── TeaFiles.csproj ├── TeaFiles.ruleset └── TeaTime.cs ├── Usage ├── Program.cs └── Usage.csproj ├── azure-pipelines.yml └── normal32.testsettings /.gitignore: -------------------------------------------------------------------------------- 1 | # Build Folders (you can keep bin if you'd like, to store dlls and pdbs) 2 | [Bb]in/ 3 | [Oo]bj/ 4 | 5 | # mstest test results 6 | TestResults 7 | 8 | ## Ignore Visual Studio temporary files, build results, and 9 | ## files generated by popular Visual Studio add-ons. 10 | 11 | # User-specific files 12 | *.suo 13 | *.user 14 | *.sln.docstates 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Rr]elease/ 19 | x64/ 20 | *_i.c 21 | *_p.c 22 | *.ilk 23 | *.meta 24 | *.obj 25 | *.pch 26 | *.pdb 27 | *.pgc 28 | *.pgd 29 | *.rsp 30 | *.sbr 31 | *.tlb 32 | *.tli 33 | *.tlh 34 | *.tmp 35 | *.log 36 | *.vspscc 37 | *.vssscc 38 | .builds 39 | 40 | # Visual C++ cache files 41 | ipch/ 42 | *.aps 43 | *.ncb 44 | *.opensdf 45 | *.sdf 46 | 47 | # Visual Studio profiler 48 | *.psess 49 | *.vsp 50 | *.vspx 51 | 52 | # Guidance Automation Toolkit 53 | *.gpState 54 | 55 | # ReSharper is a .NET coding add-in 56 | _ReSharper* 57 | 58 | # NCrunch 59 | *.ncrunch* 60 | .*crunch*.local.xml 61 | 62 | # Installshield output folder 63 | [Ee]xpress 64 | 65 | # DocProject is a documentation generator add-in 66 | DocProject/buildhelp/ 67 | DocProject/Help/*.HxT 68 | DocProject/Help/*.HxC 69 | DocProject/Help/*.hhc 70 | DocProject/Help/*.hhk 71 | DocProject/Help/*.hhp 72 | DocProject/Help/Html2 73 | DocProject/Help/html 74 | 75 | # Click-Once directory 76 | publish 77 | 78 | # Publish Web Output 79 | *.Publish.xml 80 | 81 | # NuGet Packages Directory 82 | packages 83 | 84 | # Windows Azure Build Output 85 | csx 86 | *.build.csdef 87 | 88 | # Windows Store app package directory 89 | AppPackages/ 90 | 91 | # Others 92 | [Bb]in 93 | [Oo]bj 94 | sql 95 | TestResults 96 | [Tt]est[Rr]esult* 97 | *.Cache 98 | ClientBin 99 | [Ss]tyle[Cc]op.* 100 | ~$* 101 | *.dbmdl 102 | Generated_Code #added for RIA/Silverlight projects 103 | 104 | # Backup & report files from converting an old project file to a newer 105 | # Visual Studio version. Backup files are not needed, because we have git ;-) 106 | _UpgradeReport_Files/ 107 | Backup*/ 108 | UpgradeLog*.XML 109 | /.vs/* 110 | -------------------------------------------------------------------------------- /Coverage.testsettings: -------------------------------------------------------------------------------- 1 |  2 | 3 | Run tests and test code coverage. 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |
20 |
21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /Examples/AnalyzeTicks/AnalyzeTicks.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | x86 6 | 8.0.30703 7 | 2.0 8 | {35B0A885-3123-43B7-8DCA-B4CAD7B5AD41} 9 | Exe 10 | Properties 11 | ACME.Examples 12 | AnalyzeTicks 13 | v4.7.2 14 | 15 | 16 | 512 17 | 18 | 19 | true 20 | ..\..\Bin\ 21 | DEBUG;TRACE 22 | full 23 | AnyCPU 24 | ..\..\Bin\AnalyzeTicks.exe.CodeAnalysisLog.xml 25 | true 26 | GlobalSuppressions.cs 27 | prompt 28 | MinimumRecommendedRules.ruleset 29 | ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets 30 | true 31 | ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules 32 | true 33 | true 34 | false 35 | 36 | 37 | ..\..\Bin\ 38 | TRACE 39 | true 40 | pdbonly 41 | AnyCPU 42 | ..\..\Bin\AnalyzeTicks.exe.CodeAnalysisLog.xml 43 | true 44 | GlobalSuppressions.cs 45 | prompt 46 | MinimumRecommendedRules.ruleset 47 | ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets 48 | ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules 49 | true 50 | true 51 | false 52 | false 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | {1CADE377-2821-480D-A0E7-34F224886AF6} 69 | TeaFiles 70 | 71 | 72 | {DD952649-AD45-4B89-B87E-28F0B876171E} 73 | ItemTypes 74 | 75 | 76 | 77 | 78 | 79 | 80 | 87 | -------------------------------------------------------------------------------- /Examples/AnalyzeTicks/Program.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | using System.Linq; 6 | using TeaTime; 7 | 8 | namespace ACME.Examples 9 | { 10 | class Program 11 | { 12 | class TradingSession 13 | { 14 | public Time Begin; 15 | public Time End; 16 | public int TickCount; 17 | 18 | public TradingSession(Time time) 19 | { 20 | this.Begin = time.Date; 21 | this.End = this.Begin.AddDays(1); 22 | } 23 | 24 | public override string ToString() 25 | { 26 | return this.Begin + " " + this.TickCount; 27 | } 28 | } 29 | 30 | static void Main(string[] args) 31 | { 32 | try 33 | { 34 | if (args.Count() < 1) throw new Exception("Usage: AnalyzeTicks [m]emorymapped|read [n]times"); 35 | string filename = args[0]; 36 | bool memoryMapped = args.Count() >= 2 37 | ? args[1].StartsWith("m", StringComparison.InvariantCultureIgnoreCase) 38 | : false; 39 | int n = args.Count() >= 3 ? int.Parse(args[2]) : 1; 40 | 41 | Console.WriteLine(Environment.Is64BitProcess ? "64bit" : "32bit"); 42 | 43 | for (int i = 1; i <= n; i++) 44 | { 45 | Console.WriteLine("----------------------------------"); 46 | Console.WriteLine(i + ". run"); 47 | var sw = Stopwatch.StartNew(); 48 | Run(filename, memoryMapped, n == 1); 49 | sw.Stop(); 50 | Console.WriteLine("execution time = " + sw.Elapsed.TotalMilliseconds + "ms"); 51 | } 52 | } 53 | catch (Exception ex) 54 | { 55 | Console.WriteLine(ex.Message); 56 | } 57 | } 58 | 59 | class Statistics 60 | { 61 | public double MinPrice; 62 | public double MaxPrice; 63 | public List Sessions; 64 | 65 | public Statistics() 66 | { 67 | this.Sessions = new List(); 68 | } 69 | } 70 | 71 | static void Run(string filename, bool memoryMapped, bool displayValues) 72 | { 73 | using (var tf = TeaFile.OpenRead(filename, ItemDescriptionElements.None)) 74 | { 75 | if (tf.Items.Count == 0) throw new Exception("File holds no items."); 76 | } 77 | 78 | Statistics stats; 79 | if (memoryMapped) 80 | { 81 | stats = GetSessionDataMemMapped(filename); 82 | } 83 | else 84 | { 85 | stats = GetSessionData(filename); 86 | } 87 | 88 | int minTransactions = int.MaxValue; 89 | int maxTransactions = int.MinValue; 90 | foreach (var session in stats.Sessions) 91 | { 92 | minTransactions = Math.Min(minTransactions, session.TickCount); 93 | maxTransactions = Math.Max(maxTransactions, session.TickCount); 94 | } 95 | Console.WriteLine("min price = " + stats.MinPrice); 96 | Console.WriteLine("max price = " + stats.MaxPrice); 97 | Console.WriteLine("min ticks per session = " + minTransactions); 98 | Console.WriteLine("max ticks per session = " + maxTransactions); 99 | 100 | var tickCounts = stats.Sessions.Select(s => s.TickCount).ToArray(); 101 | Array.Sort(tickCounts); 102 | int median = tickCounts[tickCounts.Length / 2]; 103 | Console.WriteLine("median = " + median); 104 | 105 | if (displayValues) 106 | { 107 | double minimumExpectedTicksPerSession = median / 2.0; 108 | Console.WriteLine("First 10 sessions:"); 109 | foreach (var s in stats.Sessions.Take(10)) 110 | { 111 | Console.WriteLine(s + " " + ((s.TickCount >= minimumExpectedTicksPerSession) ? "OK" : "QUESTIONABLE")); 112 | } 113 | } 114 | } 115 | 116 | static Statistics GetSessionDataMemMapped(string filename) 117 | { 118 | using (var mf = RawMemoryMapping.OpenRead(filename)) 119 | unsafe 120 | { 121 | var stats = new Statistics(); 122 | Tick* firstTick = (Tick*)mf.ItemAreaStart; 123 | double minPrice = firstTick->Price; 124 | double maxPrice = firstTick->Price; 125 | var session = new TradingSession(firstTick->Time); 126 | stats.Sessions.Add(session); 127 | for (var t = firstTick; t != mf.ItemAreaEnd; t++) 128 | { 129 | if (t->Time >= session.End) 130 | { 131 | session = new TradingSession(t->Time); 132 | stats.Sessions.Add(session); 133 | } 134 | session.TickCount++; 135 | minPrice = Math.Min(t->Price, minPrice); 136 | maxPrice = Math.Max(t->Price, maxPrice); 137 | } 138 | stats.MinPrice = minPrice; 139 | stats.MaxPrice = maxPrice; 140 | return stats; 141 | } 142 | } 143 | 144 | static Statistics GetSessionData(string filename) 145 | { 146 | using (var tf = TeaFile.OpenRead(filename, ItemDescriptionElements.None)) 147 | { 148 | var stats = new Statistics(); 149 | Tick firstTick = tf.Items.First(); 150 | double minPrice = firstTick.Price; 151 | double maxPrice = firstTick.Price; 152 | var session = new TradingSession(firstTick.Time); 153 | stats.Sessions.Add(session); 154 | int n = 0; 155 | foreach (var tick in tf.Items) 156 | { 157 | if (tick.Time >= session.End) 158 | { 159 | session = new TradingSession(tick.Time); 160 | stats.Sessions.Add(session); 161 | } 162 | session.TickCount++; 163 | 164 | minPrice = Math.Min(tick.Price, minPrice); 165 | maxPrice = Math.Max(tick.Price, maxPrice); 166 | n++; 167 | } 168 | stats.MinPrice = minPrice; 169 | stats.MaxPrice = maxPrice; 170 | return stats; 171 | } 172 | } 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /Examples/Benchmarks/Benchmarks.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | x86 6 | 8.0.30703 7 | 2.0 8 | {E1A16FF5-631B-4CC9-848F-61FF1EA3B961} 9 | Exe 10 | Properties 11 | TeaTime.Benchmarks 12 | Benchmarks 13 | v4.7.2 14 | 15 | 16 | 512 17 | 18 | 19 | x86 20 | true 21 | full 22 | false 23 | bin\Debug\ 24 | DEBUG;TRACE 25 | prompt 26 | 4 27 | false 28 | 29 | 30 | x86 31 | pdbonly 32 | true 33 | bin\Release\ 34 | TRACE 35 | prompt 36 | 4 37 | false 38 | 39 | 40 | true 41 | ..\..\Bin\ 42 | DEBUG;TRACE 43 | full 44 | AnyCPU 45 | bin\Debug\DiscreteLogics.TeaFile.Benchmarks.exe.CodeAnalysisLog.xml 46 | true 47 | GlobalSuppressions.cs 48 | prompt 49 | MinimumRecommendedRules.ruleset 50 | ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets 51 | true 52 | ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules 53 | true 54 | false 55 | true 56 | false 57 | 58 | 59 | ..\..\Bin\ 60 | TRACE 61 | true 62 | pdbonly 63 | AnyCPU 64 | bin\Release\DiscreteLogics.TeaFile.Benchmarks.exe.CodeAnalysisLog.xml 65 | true 66 | GlobalSuppressions.cs 67 | prompt 68 | MinimumRecommendedRules.ruleset 69 | ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets 70 | true 71 | ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules 72 | true 73 | true 74 | false 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | PreserveNewest 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | {1CADE377-2821-480D-A0E7-34F224886AF6} 105 | TeaFiles 106 | 107 | 108 | {DD952649-AD45-4B89-B87E-28F0B876171E} 109 | ItemTypes 110 | 111 | 112 | 113 | 120 | -------------------------------------------------------------------------------- /Examples/Benchmarks/ComputerSpecs.cs: -------------------------------------------------------------------------------- 1 | // code adopted from MeasureIt by Vance Morrison - http://blogs.msdn.com/b/vancem/archive/2009/02/06/measureit-update-tool-for-doing-microbenchmarks.aspx 2 | using System.Management; 3 | 4 | namespace TeaTime.Benchmarks 5 | { 6 | public class ComputerSpecs 7 | { 8 | public string Name; 9 | public string Manufacturer; 10 | public string Model; 11 | 12 | public string OperatingSystem; 13 | public string OperatingSystemVersion; 14 | public int OperatingSystemServicePack; 15 | 16 | public int NumberOfDisks; 17 | public string SystemDiskModel; 18 | 19 | public int NumberOfProcessors; 20 | public string ProcessorName; 21 | public string ProcessorDescription; 22 | public int ProcessorClockSpeedMhz; 23 | 24 | public int MemoryMBytes; 25 | public int L1KBytes; 26 | public int L2KBytes; 27 | 28 | public ComputerSpecs() 29 | { 30 | ManagementObjectSearcher searcher = new ManagementObjectSearcher("Select * from Win32_ComputerSystem"); 31 | foreach (ManagementObject mo in searcher.Get()) 32 | { 33 | Name = (string)mo["Caption"]; 34 | Manufacturer = (string)mo["Manufacturer"]; 35 | Model = (string)mo["Model"]; 36 | MemoryMBytes = (int)(((ulong)mo["TotalPhysicalMemory"]) / (1024 * 1024)); 37 | } 38 | 39 | searcher = new ManagementObjectSearcher("Select * from Win32_OperatingSystem"); 40 | foreach (ManagementObject mo in searcher.Get()) 41 | { 42 | OperatingSystem = (string)mo["Caption"]; 43 | OperatingSystemVersion = (string)mo["Version"]; 44 | OperatingSystemServicePack = (int)(ushort)mo["ServicePackMajorVersion"]; 45 | break; 46 | } 47 | 48 | searcher = new ManagementObjectSearcher("Select * from Win32_DiskDrive"); 49 | ManagementObjectCollection disks = searcher.Get(); 50 | NumberOfDisks = disks.Count; 51 | foreach (ManagementObject mo in disks) 52 | { 53 | SystemDiskModel = (string)mo["Caption"]; 54 | break; 55 | } 56 | 57 | searcher = new ManagementObjectSearcher("Select * from Win32_Processor"); 58 | ManagementObjectCollection processors = searcher.Get(); 59 | NumberOfProcessors = processors.Count; 60 | foreach (ManagementObject mo in processors) 61 | { 62 | ProcessorName = (string)mo["Name"]; 63 | ProcessorDescription = (string)mo["Description"]; 64 | ProcessorClockSpeedMhz = (int)(uint)mo["MaxClockSpeed"]; 65 | // Console.WriteLine(" NumberOfCores: " + mo["NumberOfCores"]); 66 | // Console.WriteLine(" NumberOfLogicalProcessors: " + mo["NumberOfLogicalProcessors"]); 67 | // Console.WriteLine(" L2CacheSize: " + mo["L2CacheSize"]); 68 | break; 69 | } 70 | 71 | searcher = new ManagementObjectSearcher("Select * from Win32_CacheMemory"); 72 | foreach (ManagementObject mo in searcher.Get()) 73 | { 74 | //Console.WriteLine(" Purpose: " + mo["Purpose"]); 75 | // Console.WriteLine(" InstalledSize: " + mo["InstalledSize"] + " K"); 76 | int level = (ushort)mo["Level"] - 2; 77 | // Console.WriteLine(" Level: " + level + " K"); 78 | if (level == 1) 79 | L1KBytes += (int)(uint)mo["InstalledSize"]; 80 | else if (level == 2) 81 | L2KBytes += (int)(uint)mo["InstalledSize"]; 82 | } 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Examples/Benchmarks/Program.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics © 2011 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | using System.Linq; 6 | using System.IO; 7 | using System.Management; 8 | using System.Reflection; 9 | 10 | namespace TeaTime.Benchmarks 11 | { 12 | class Program 13 | { 14 | static void Main() 15 | { 16 | const long N = 5 * 1000 * 1000; 17 | const string filename = "teafiles.benchmark.htm"; 18 | 19 | // compute the sum by formula and print it, as a check value 20 | const long target = (N * (N - 1) / 2); 21 | 22 | Runner runner = new Runner(N); 23 | runner.TargetValue = target; 24 | for (int i = 0; i < 5; i++) 25 | { 26 | Operations.RunAll(runner); 27 | } 28 | 29 | File.Delete(filename); 30 | File.WriteAllText(filename, runner.GetReport()); 31 | Process.Start(filename); 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /Examples/Benchmarks/Runner.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Text; 8 | 9 | namespace TeaTime.Benchmarks 10 | { 11 | class Run 12 | { 13 | public long N; 14 | public TimeSpan ExecutionTime; 15 | 16 | public double OperationsPerSecond 17 | { 18 | get { return N / ExecutionTime.TotalSeconds; } 19 | } 20 | } 21 | 22 | class RunSet 23 | { 24 | public List Runs { get; internal set; } 25 | public double RelativeExecutionTimePercent { get; set; } 26 | 27 | public RunSet() 28 | { 29 | this.Runs = new List(); 30 | } 31 | 32 | public double AverageSeconds() 33 | { 34 | return this.Runs.Select(r => r.ExecutionTime.TotalSeconds).Average(); 35 | } 36 | 37 | public double AverageOperationsPerSeconds(long n) 38 | { 39 | return (double)n / this.AverageSeconds(); 40 | } 41 | } 42 | 43 | class Runner 44 | { 45 | Dictionary operationruns; 46 | public long N; 47 | public long TargetValue; 48 | 49 | public Runner(long N) 50 | { 51 | this.operationruns = new Dictionary(); 52 | this.N = N; 53 | } 54 | 55 | public void Measure(string name, Action action) 56 | { 57 | RunSet runs; 58 | if (!this.operationruns.TryGetValue(name, out runs)) 59 | { 60 | this.operationruns[name] = runs = new RunSet(); 61 | } 62 | 63 | var sw = Stopwatch.StartNew(); 64 | action(); 65 | sw.Stop(); 66 | 67 | var r = new Run(); 68 | r.N = this.N; 69 | r.ExecutionTime = sw.Elapsed; 70 | runs.Runs.Add(r); 71 | } 72 | 73 | public void ComputeStats() 74 | { 75 | double maxavgseconds = this.operationruns.Select(runs => runs.Value.AverageSeconds()).Max(); 76 | foreach (var runs in operationruns) 77 | { 78 | var percent = runs.Value.AverageSeconds() / maxavgseconds * 100; 79 | percent = Math.Round(percent); 80 | runs.Value.RelativeExecutionTimePercent = percent; 81 | } 82 | } 83 | 84 | public string GetReport() 85 | { 86 | ComputeStats(); 87 | var w = new StringWriter(); 88 | using (w.Tag("table")) 89 | { 90 | w.WriteLine("benchmarkrunsrelativeops / second".Formatted(operationruns.First().Value.Runs.Count)); 91 | foreach (var run in this.operationruns) 92 | { 93 | w.WriteLine("{0}{1}{2}{3}".Formatted( 94 | run.Key, 95 | run.Value.Runs.Select(r => "{0}".Formatted(r.ExecutionTime.TotalMilliseconds.ToString("0.00"))).Joined(), 96 | "
 
".Formatted(run.Value.RelativeExecutionTimePercent), 97 | run.Value.AverageOperationsPerSeconds(this.N).ToString("#,##0") 98 | )); 99 | } 100 | } 101 | w.WriteLine(); 102 | using (w.Tag("table")) 103 | { 104 | w.TableRow("computer specs"); 105 | var spec = new ComputerSpecs(); 106 | foreach (FieldInfo field in spec.GetType().GetFields()) 107 | { 108 | w.TableRow(field.Name, field.GetValue(spec) as string); 109 | } 110 | } 111 | string html = File.ReadAllText("results.htm"); 112 | html = html.Replace("results", w.ToString()); 113 | return html; 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /Examples/Benchmarks/Utils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | 5 | namespace TeaTime.Benchmarks 6 | { 7 | static class Extensions 8 | { 9 | public static string Formatted(this string s, params object[] values) 10 | { 11 | return string.Format(s, values); 12 | } 13 | 14 | public static string Joined(this IEnumerable values, string separator = "") 15 | { 16 | return string.Join(separator, values); 17 | } 18 | 19 | public static IDisposable Tag(this TextWriter writer, string tag) 20 | { 21 | writer.Write("<" + tag + ">"); 22 | return new Disposable(() => writer.Write("")); 23 | } 24 | 25 | public static TextWriter TableRow(this TextWriter writer, params string[] tabledata) 26 | { 27 | using (writer.Tag("tr")) 28 | { 29 | foreach (var s in tabledata) 30 | { 31 | using (writer.Tag("td")) 32 | { 33 | writer.Write(s); 34 | } 35 | } 36 | } 37 | writer.WriteLine(); 38 | return writer; 39 | } 40 | } 41 | 42 | class Disposable : IDisposable 43 | { 44 | Action action; 45 | 46 | public Disposable(Action a) 47 | { 48 | this.action = a; 49 | } 50 | 51 | public void Dispose() 52 | { 53 | this.action(); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Examples/Benchmarks/results.htm: -------------------------------------------------------------------------------- 1 |  TeaFile API - benchmark 28 | 29 | results 30 | 31 | -------------------------------------------------------------------------------- /Examples/CreateSessions/CreateSessions.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | x86 6 | 8.0.30703 7 | 2.0 8 | {8CE80E28-D365-46EE-805C-E54550793867} 9 | Exe 10 | Properties 11 | ACME.Examples 12 | CreateSessions 13 | v4.7.2 14 | 15 | 16 | 512 17 | 18 | 19 | true 20 | ..\..\Bin\ 21 | DEBUG;TRACE 22 | full 23 | AnyCPU 24 | ..\..\Bin\CreateTicks.exe.CodeAnalysisLog.xml 25 | true 26 | GlobalSuppressions.cs 27 | prompt 28 | MinimumRecommendedRules.ruleset 29 | ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets 30 | ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules 31 | false 32 | false 33 | false 34 | false 35 | 36 | 37 | ..\..\Bin\ 38 | TRACE 39 | true 40 | pdbonly 41 | AnyCPU 42 | ..\..\Bin\CreateTicks.exe.CodeAnalysisLog.xml 43 | true 44 | GlobalSuppressions.cs 45 | prompt 46 | MinimumRecommendedRules.ruleset 47 | ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets 48 | false 49 | ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules 50 | false 51 | false 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | {1CADE377-2821-480D-A0E7-34F224886AF6} 68 | TeaFiles 69 | 70 | 71 | {DD952649-AD45-4B89-B87E-28F0B876171E} 72 | ItemTypes 73 | 74 | 75 | 76 | 77 | 78 | 79 | 86 | -------------------------------------------------------------------------------- /Examples/CreateSessions/Program.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | using System; 3 | using System.IO; 4 | using TeaTime; 5 | 6 | namespace ACME.Examples 7 | { 8 | class Program 9 | { 10 | static Random r; 11 | 12 | static bool DrawRandom(double percentageProbability) 13 | { 14 | return r.Next(0, 99) < percentageProbability; 15 | } 16 | 17 | /// 18 | /// This program creates a TeaFile that holds ticks on n days. 90% of the days will be good days, holding around 1000 ticks, 19 | /// while the other 10% are bad days that hold only 1% of that number, so about 10 ticks. 20 | /// Such sample file can then be used to run the AnalyzeTicks program that detects errors in data files. 21 | /// 22 | static void Main(string[] args) 23 | { 24 | try 25 | { 26 | if (args.Length != 2) throw new Exception("Usage: CreateSessions "); 27 | string filename = args[0]; 28 | int n = int.Parse(args[1]); 29 | CreateSampleFile(filename, n); 30 | } 31 | catch (Exception ex) 32 | { 33 | Console.WriteLine(ex.Message); 34 | } 35 | } 36 | 37 | static void CreateSampleFile(string filename, int n) 38 | { 39 | r = new Random(1); 40 | var t = new Time(2000, 1, 1); 41 | File.Delete(filename); 42 | using (var tf = TeaFile.Create(filename)) 43 | { 44 | while (n-- > 0) 45 | { 46 | bool isGoodDay = DrawRandom(90); 47 | WriteDailyTicks(tf, t, isGoodDay); 48 | t = t.AddDays(1); 49 | } 50 | } 51 | } 52 | 53 | static void WriteDailyTicks(TeaFile tf, Time day, bool isGoodDay) 54 | { 55 | Time t = day.AddHours(9); // start trading session at 09:00 56 | Time end = day.AddHours(17.5); // end trading session at 17:30 57 | while (t < end) 58 | { 59 | // on a good day, we write many ticks, on a bad day however only 1 percent as much 60 | if (isGoodDay || DrawRandom(1)) 61 | { 62 | double p = r.NextDouble() * 100000.0; 63 | tf.Write(new Tick {Time = t, Price = p, Volume = 10}); 64 | } 65 | t = t.AddSeconds(15 + r.Next(0, 20)); 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Examples/CreateTicks/CreateTicks.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | x86 6 | 8.0.30703 7 | 2.0 8 | {1953EC93-0DD1-4433-A00F-7A146DA8793E} 9 | Exe 10 | Properties 11 | ACME.Examples 12 | CreateTicks 13 | v4.7.2 14 | 15 | 16 | 512 17 | 18 | 19 | true 20 | ..\..\Bin\ 21 | DEBUG;TRACE 22 | full 23 | AnyCPU 24 | bin\Debug\CreateRandomSeries.exe.CodeAnalysisLog.xml 25 | true 26 | GlobalSuppressions.cs 27 | prompt 28 | MinimumRecommendedRules.ruleset 29 | ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets 30 | true 31 | ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules 32 | true 33 | false 34 | 35 | 36 | ..\..\Bin\ 37 | TRACE 38 | true 39 | pdbonly 40 | AnyCPU 41 | bin\Release\CreateRandomSeries.exe.CodeAnalysisLog.xml 42 | true 43 | GlobalSuppressions.cs 44 | prompt 45 | MinimumRecommendedRules.ruleset 46 | ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets 47 | ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules 48 | false 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | {1CADE377-2821-480D-A0E7-34F224886AF6} 65 | TeaFiles 66 | 67 | 68 | {DD952649-AD45-4B89-B87E-28F0B876171E} 69 | ItemTypes 70 | 71 | 72 | 73 | 74 | 75 | 76 | 83 | -------------------------------------------------------------------------------- /Examples/CreateTicks/Program.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | using System; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using TeaTime; 6 | 7 | namespace ACME.Examples 8 | { 9 | class Program 10 | { 11 | static void Main(string[] args) 12 | { 13 | try 14 | { 15 | if (args.Count() != 2) 16 | { 17 | Console.WriteLine("Usage: CreateTicks "); 18 | return; 19 | } 20 | string filename = args[0]; 21 | int n = int.Parse(args[1]); 22 | 23 | var sw = Stopwatch.StartNew(); 24 | 25 | Time t = DateTime.Now; 26 | using (var tf = TeaFile.Create(filename)) 27 | { 28 | tf.Write(Enumerable.Range(1, n).Select(i => new Tick {Time = t.AddDays(i), Price = i * 101.0, Volume = i * 1000})); 29 | } 30 | 31 | sw.Stop(); 32 | Console.WriteLine("execution time = " + sw.Elapsed.TotalMilliseconds + "ms"); 33 | } 34 | catch (Exception ex) 35 | { 36 | Console.WriteLine(ex.Message); 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Examples/GetSnapshot/GetSnapshot.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | x86 6 | 8.0.30703 7 | 2.0 8 | {AFE98971-4858-4F1F-9E38-435EC3E6D225} 9 | Exe 10 | Properties 11 | ACME.Examples 12 | GetSnapshot 13 | v4.7.2 14 | 15 | 16 | 512 17 | 18 | 19 | true 20 | ..\..\Bin\ 21 | DEBUG;TRACE 22 | full 23 | AnyCPU 24 | ..\..\Bin\GetSnapshot.exe.CodeAnalysisLog.xml 25 | true 26 | GlobalSuppressions.cs 27 | prompt 28 | MinimumRecommendedRules.ruleset 29 | ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets 30 | true 31 | ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules 32 | false 33 | 34 | 35 | ..\..\Bin\ 36 | TRACE 37 | true 38 | pdbonly 39 | AnyCPU 40 | ..\..\Bin\GetSnapshot.exe.CodeAnalysisLog.xml 41 | true 42 | GlobalSuppressions.cs 43 | prompt 44 | MinimumRecommendedRules.ruleset 45 | ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets 46 | true 47 | ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules 48 | true 49 | false 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | {1CADE377-2821-480D-A0E7-34F224886AF6} 66 | TeaFiles 67 | 68 | 69 | {DD952649-AD45-4B89-B87E-28F0B876171E} 70 | ItemTypes 71 | 72 | 73 | 74 | 75 | 76 | 77 | 84 | -------------------------------------------------------------------------------- /Examples/GetSnapshot/Program.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | using System; 3 | using System.Linq; 4 | using TeaTime; 5 | 6 | namespace ACME.Examples 7 | { 8 | class Program 9 | { 10 | static void Main(string[] args) 11 | { 12 | try 13 | { 14 | if (args.Count() != 1) throw new Exception("Usage: CreateTicks "); 15 | string filename = args.First(); 16 | 17 | using (var tf = TeaFile.OpenRead(filename)) 18 | { 19 | Console.WriteLine(tf.Description); 20 | Console.WriteLine("ItemAreaStart={0}", tf.ItemAreaStart); 21 | Console.WriteLine("ItemAreaEnd={0}", tf.ItemAreaEnd); 22 | Console.WriteLine("ItemAreaSize={0}", tf.ItemAreaSize); 23 | foreach (Tick tick in tf.Items.Take(5)) 24 | { 25 | Console.WriteLine(tick); 26 | } 27 | } 28 | } 29 | catch (Exception ex) 30 | { 31 | Console.WriteLine(ex.Message); 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Examples/ItemTypes/ItemTypes.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | using TeaTime; 3 | 4 | namespace ACME.Examples 5 | { 6 | public struct Tick 7 | { 8 | [EventTime] 9 | public Time Time; 10 | 11 | public double Price; 12 | public int Volume; 13 | 14 | public override string ToString() 15 | { 16 | return "Time=" + this.Time + " Price=" + this.Price + " Volume=" + this.Volume; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Examples/ItemTypes/ItemTypes.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | 8.0.30703 7 | 2.0 8 | {DD952649-AD45-4B89-B87E-28F0B876171E} 9 | Library 10 | Properties 11 | ACME.Examples 12 | ItemTypes 13 | v4.7.2 14 | 512 15 | 16 | 17 | 18 | true 19 | full 20 | false 21 | ..\..\Bin\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | false 26 | 27 | 28 | pdbonly 29 | true 30 | ..\..\Bin\ 31 | TRACE 32 | prompt 33 | 4 34 | false 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | {1CADE377-2821-480D-A0E7-34F224886AF6} 51 | TeaFiles 52 | 53 | 54 | 55 | 56 | 57 | 58 | 65 | -------------------------------------------------------------------------------- /Examples/Sum/Program.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | using System; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using TeaTime; 6 | 7 | namespace ACME.Examples 8 | { 9 | class Program 10 | { 11 | static void Main(string[] args) 12 | { 13 | try 14 | { 15 | if (args.Count() < 1) throw new Exception("Usage: Sum [m]emorymapped"); 16 | string filename = args[0]; 17 | bool memoryMapped = args.Count() >= 2 18 | ? args[1].StartsWith("m", StringComparison.InvariantCultureIgnoreCase) 19 | : false; 20 | 21 | var sw = Stopwatch.StartNew(); 22 | double sum; 23 | if (memoryMapped) 24 | { 25 | sum = RunMemoryMapped(filename); 26 | } 27 | else 28 | { 29 | sum = Run(filename); 30 | } 31 | sw.Stop(); 32 | Console.WriteLine("sum=" + sum); 33 | Console.WriteLine("execution time = " + sw.Elapsed.TotalMilliseconds + "ms"); 34 | } 35 | catch (Exception ex) 36 | { 37 | Console.WriteLine(ex.Message); 38 | } 39 | } 40 | 41 | static double Run(string filename) 42 | { 43 | using (var tf = TeaFile.OpenRead(filename)) 44 | { 45 | return tf.Items.Sum(item => item.Price); 46 | } 47 | } 48 | 49 | static unsafe double RunMemoryMapped(string filename) 50 | { 51 | double sum = 0; 52 | using (var fm = TeaFile.OpenRawMemoryMapping(filename)) 53 | { 54 | for (Tick* tick = (Tick*)fm.ItemAreaStart; tick != fm.ItemAreaEnd; ++tick) 55 | { 56 | sum += tick->Price; 57 | } 58 | } 59 | return sum; 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Examples/Sum/Sum.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | x86 6 | 8.0.30703 7 | 2.0 8 | {33331A44-5C87-4931-9B34-C1B4A490DBFA} 9 | Exe 10 | Properties 11 | ACME.Examples 12 | Sum 13 | v4.7.2 14 | 15 | 16 | 512 17 | 18 | 19 | true 20 | ..\..\Bin\ 21 | DEBUG;TRACE 22 | full 23 | AnyCPU 24 | ..\..\Bin\Sum.exe.CodeAnalysisLog.xml 25 | true 26 | GlobalSuppressions.cs 27 | prompt 28 | MinimumRecommendedRules.ruleset 29 | ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets 30 | true 31 | ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules 32 | true 33 | true 34 | false 35 | 36 | 37 | ..\..\Bin\ 38 | 39 | 40 | true 41 | pdbonly 42 | AnyCPU 43 | ..\..\Bin\Sum.exe.CodeAnalysisLog.xml 44 | true 45 | GlobalSuppressions.cs 46 | prompt 47 | MinimumRecommendedRules.ruleset 48 | ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets 49 | true 50 | ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules 51 | true 52 | false 53 | true 54 | false 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | {1CADE377-2821-480D-A0E7-34F224886AF6} 71 | TeaFiles 72 | 73 | 74 | {DD952649-AD45-4B89-B87E-28F0B876171E} 75 | ItemTypes 76 | 77 | 78 | 79 | 80 | 81 | 82 | 89 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2012-present Thomas Hulka 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 | -------------------------------------------------------------------------------- /Lib/Moq.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discretelogics/TeaFiles.Net-Time-Series-Storage-in-Files/7a0aef4ed5fda31fdfcd45e1ca565977a4315530/Lib/Moq.dll -------------------------------------------------------------------------------- /Lib/SharpTestsEx.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/discretelogics/TeaFiles.Net-Time-Series-Storage-in-Files/7a0aef4ed5fda31fdfcd45e1ca565977a4315530/Lib/SharpTestsEx.dll -------------------------------------------------------------------------------- /Normal.testsettings: -------------------------------------------------------------------------------- 1 |  2 | 3 | These are default test settings for a local test run. 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 |
19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | TeaFiles.Net - Time Series Storage in Files 2 | =========================================== 3 | 4 | Use TeaFiles.Net to create, read and write files holding time series data. 5 | 6 | 7 | 8 | Features 9 | -------- 10 | 11 | * highest **performance** 12 | * very **simple** 13 | * **versatile** - open TeaFiles with C++, C#, Python, R, on any platform 14 | 15 | 16 | 17 | TeaFiles = Binary Data + Header 18 | ------------------------------- 19 | 20 | * TeaFile is a **file format** 21 | * to store **time series** 22 | * as binary **flat files** 23 | * optional **header** holds a **description** of file contents (metadata) 24 | * including a description of the **item type layout** (schema) 25 | * simple format, designed to make API writing very easy 26 | * http://www.discretelogics.com published the file format and 27 | * releases APIs for C#, C++, Python under the GPL 28 | 29 | 30 | 31 | Performance 32 | ----------- 33 | > 250 Mio Items per seconds cold 34 | > 465 Mio Items per seconds warm 35 | 36 | benchmark results at http://www.discretelogics.com/resources/benchmarks/teafilesbm.htm 37 | 38 | 39 | 40 | Installation 41 | ------------ 42 | 43 | * Get the NuGet package from nuget.org/packages/TeaFiles.Net or 44 | 45 | 46 | Documentation 47 | ------------- 48 | 49 | The API class documentation is available online at www.discretelogics.com/doc/teafiles.net 50 | 51 | TeaTime product family 52 | ---------------------- 53 | 54 | TeaFiles are the foundation of the TeaTime product family, the toolset for persistence, visualisation and real time analysis of time series. 55 | 56 | Related products: 57 | * [TeaShell](http://www.discretelogics.com/teashell) - Browse Time Series like Photos 58 | 59 | Examples 60 | -------- 61 | 62 | ```C# 63 | // define time series item type 64 | struct Tick 65 | { 66 | public Time Time; 67 | public double Price; 68 | public int Volume; 69 | } 70 | // create file and write values 71 | using (var tf = TeaFile.Create("acme.tea")) 72 | { 73 | tf.Write(new Tick { Price = 5, Time = DateTime.Now, Volume = 700 }); 74 | tf.Write(new Tick { Price = 15, Time = DateTime.Now.AddHours(1), Volume = 1700 }); 75 | // ... 76 | } 77 | // sum the prices of all items in the file 78 | using (var tf = TeaFile.OpenRead("acme.tea")) 79 | { 80 | return tf.Items.Sum(item => item.Price); 81 | } 82 | ``` 83 | 84 | License 85 | ------- 86 | 87 | Released under the MIT license. 88 | 89 | Thanks 90 | ------ 91 | 92 | to contributors @larstbone and @SabotageAndi 93 | -------------------------------------------------------------------------------- /TeaFiles.Test/Access/ItemCollectionTest.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | using System.Collections; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Threading; 6 | using Microsoft.VisualStudio.TestTools.UnitTesting; 7 | using Moq; 8 | using SharpTestsEx; 9 | 10 | namespace TeaTime 11 | { 12 | [TestClass] 13 | public class ItemCollectionTest 14 | { 15 | [TestInitialize] 16 | public void Init() 17 | { 18 | // Ensure english error messages on non english systems. 19 | Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US"); 20 | } 21 | 22 | [TestMethod] 23 | public void ExoticCoverage() 24 | { 25 | var r = new Mock(MockBehavior.Strict); 26 | var collection = new ItemCollection(r.Object); 27 | IEnumerable enumerable = collection; 28 | enumerable.GetEnumerator().Should().Not.Be.Null(); 29 | } 30 | 31 | [TestMethod] 32 | public void GetEnumeratorTest() 33 | { 34 | var filename = "ItemCollectionTest_GetEnumeratorTest.tea"; 35 | using (var tf = TeaFile.Create(filename)) 36 | { 37 | tf.Write(Enumerable.Range(2, 4)); 38 | } 39 | using (var tf = TeaFile.OpenRead(filename)) 40 | { 41 | var e = tf.Items.GetEnumerator(0); 42 | e.MoveNext().Should().Be.True(); 43 | e.Current.Values[0].Should().Be(2); 44 | e.MoveNext().Should().Be.True(); 45 | e.Current.Values[0].Should().Be(3); 46 | e.MoveNext().Should().Be.True(); 47 | e.Current.Values[0].Should().Be(4); 48 | e.MoveNext().Should().Be.True(); 49 | e.Current.Values[0].Should().Be(5); 50 | e.MoveNext().Should().Be.False(); 51 | 52 | e = tf.Items.GetEnumerator(2); 53 | e.MoveNext().Should().Be.True(); 54 | e.Current.Values[0].Should().Be(4); 55 | e.MoveNext().Should().Be.True(); 56 | e.Current.Values[0].Should().Be(5); 57 | e.MoveNext().Should().Be.False(); 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /TeaFiles.Test/Access/ManagedMemoryMappingTest.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | using System; 3 | using System.Globalization; 4 | using System.Threading; 5 | using Microsoft.VisualStudio.TestTools.UnitTesting; 6 | using SharpTestsEx; 7 | 8 | namespace TeaTime 9 | { 10 | [TestClass] 11 | public class MemoryMappedAccessTest 12 | { 13 | [TestInitialize] 14 | public void Init() 15 | { 16 | // Ensure english error messages on non english systems. 17 | Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US"); 18 | } 19 | 20 | [TestMethod] 21 | public void AccessItemsViaMemoryMapping() 22 | { 23 | var filename = "MemoryMappedAccessTest_AccessItemsViaMemoryMapping.tea"; 24 | using (var f = TeaFile>.Create(filename)) 25 | { 26 | var dt = new DateTime(2000, 1, 1); 27 | for (int i = 0; i < 27; i++) 28 | { 29 | f.Write(new Event {Time = dt.AddDays(i), Value = i * 10}); 30 | } 31 | } 32 | using (var view = TeaFile>.OpenMemoryMapping(filename)) 33 | { 34 | Event e = view.Read(0); 35 | e.Value.Should().Be(0); 36 | e.Time.Should().Be(new DateTime(2000, 1, 1)); 37 | 38 | e = view.Read(0); 39 | e.Value.Should().Be(0); 40 | e.Time.Should().Be(new DateTime(2000, 1, 1)); 41 | 42 | e = view.Read(1); 43 | e.Value.Should().Be(10); 44 | e.Time.Should().Be(new DateTime(2000, 1, 2)); 45 | 46 | e = view.Read(2); 47 | e.Value.Should().Be(20); 48 | e.Time.Should().Be(new DateTime(2000, 1, 3)); 49 | 50 | var t = new DateTime(2000, 1, 1); 51 | for (int i = 0; i < 27; i++) 52 | { 53 | e = view.Read(i); 54 | e.Value.Should().Be(i * 10); 55 | e.Time.Should().Be(t.AddDays(i)); 56 | e.Should().Be(view[i]); 57 | } 58 | } 59 | } 60 | 61 | [TestMethod] 62 | public void ReadingBeyondEntOfItemArea() 63 | { 64 | var filename = "MemoryMappedAccessTest_ReadingBeyondEntOfItemArea.tea"; 65 | using (var f = TeaFile>.Create(filename)) 66 | { 67 | Time t = new DateTime(2000, 1, 1); 68 | 3.Times(() => f.Write(new Event {Time = t, Value = 777})); 69 | } 70 | using (var view = TeaFile>.OpenMemoryMapping(filename)) 71 | { 72 | view.Read(0); 73 | view.Read(1); 74 | view.Read(2); 75 | Executing.This(() => view.Read(3)).Should().Throw(); 76 | } 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /TeaFiles.Test/Access/RawMemoryMappingTest.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | using System; 3 | using System.Globalization; 4 | using System.Threading; 5 | using Microsoft.VisualStudio.TestTools.UnitTesting; 6 | using SharpTestsEx; 7 | using TeaTime.SampleItems; 8 | 9 | namespace TeaTime.Access 10 | { 11 | [TestClass] 12 | public class RawMemoryMappingTest 13 | { 14 | [TestInitialize] 15 | public void Init() 16 | { 17 | // Ensure english error messages on non english systems. 18 | Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US"); 19 | } 20 | 21 | [TestMethod] 22 | public unsafe void AccessItemsViaMemoryMapping() 23 | { 24 | const string filename = "RawMemoryMappingTest_AccessItemsViaMemoryMapping.tea"; 25 | using (var f = TeaFile.Create(filename)) 26 | { 27 | var dt = new DateTime(2000, 1, 1); 28 | for (int i = 0; i < 27; i++) 29 | { 30 | f.Write(new Tick {Time = dt.AddDays(i), Volume = i * 10}); 31 | } 32 | } 33 | using (var view = TeaFile.OpenRawMemoryMapping(filename)) 34 | { 35 | Tick* tick = (Tick*)view.ItemAreaStart; 36 | var dt = new DateTime(2000, 1, 1); 37 | for (int i = 0; i < 27; i++) 38 | { 39 | tick->Volume.Should().Be(i * 10); 40 | tick->Time.Should().Be(dt.AddDays(i)); 41 | tick++; 42 | } 43 | } 44 | } 45 | 46 | [TestMethod] 47 | public unsafe void AccessItemsViaMemoryMappingPointers() 48 | { 49 | const string filename = "AccessItemsViaMemoryMappingPointers.tea"; 50 | using (var f = TeaFile.Create(filename)) 51 | { 52 | var dt = new DateTime(2000, 1, 1); 53 | for (int i = 0; i < 3; i++) 54 | { 55 | f.Write(new Tick { Time = dt.AddDays(i), Volume = i * 10 }); 56 | } 57 | } 58 | using (var view = TeaFile.OpenRawMemoryMapping(filename)) 59 | { 60 | Tick* tick = (Tick*)view.ItemAreaStart; 61 | Tick* end = (Tick*)view.ItemAreaEnd; 62 | ((int)tick).Should().Be.LessThanOrEqualTo((int)end); 63 | 64 | int i = 0; 65 | var dt = new DateTime(2000, 1, 1); 66 | while(tick < end) 67 | { 68 | tick->Volume.Should().Be(i * 10); 69 | tick->Time.Should().Be(dt.AddDays(i)); 70 | tick++; 71 | i++; 72 | } 73 | } 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /TeaFiles.Test/Access/SafeBufferTest.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | 4 | namespace TeaTime 5 | { 6 | [TestClass] 7 | public class SafeBufferTest 8 | { 9 | // tests adopted from CppUtilsTest 10 | //[TestMethod] 11 | //unsafe public void WriteStructIntoFileTest() 12 | //{ 13 | // using (var f = new FileStream("test", FileMode.Create)) 14 | // { 15 | // var u = new CppUtils(f); 16 | // A a = new A() { d = 1.7, i16 = 1, ui8 = 32 }; 17 | // u.Write(a); 18 | // Assert.AreEqual(sizeof(A), f.Position); 19 | // } 20 | //} 21 | 22 | //[TestMethod] 23 | //unsafe public void WriteAndReadStructIntoFileTest() 24 | //{ 25 | // using (var f = new FileStream("test", FileMode.Create)) 26 | // { 27 | // var u = new CppUtils(f); 28 | // A a = new A() { d = 1.7, i16 = 1, ui8 = 32 }; 29 | // u.Write(a); 30 | // f.Position = 0; // rewind 31 | // A aa = u.Read(); 32 | // Assert.AreEqual(aa, a); 33 | // } 34 | //} 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /TeaFiles.Test/Base/EventTest.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | using System; 3 | using System.Globalization; 4 | using System.Threading; 5 | using Microsoft.VisualStudio.TestTools.UnitTesting; 6 | using SharpTestsEx; 7 | 8 | namespace TeaTime 9 | { 10 | [TestClass] 11 | public class EventTest 12 | { 13 | [TestInitialize] 14 | public void Init() 15 | { 16 | // Ensure english error messages on non english systems. 17 | Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US"); 18 | } 19 | 20 | [TestMethod] 21 | public void EventToString() 22 | { 23 | Event e = new Event(); 24 | e.Time = new Time(2000, 1, 2); 25 | e.Value = 172; 26 | 27 | string datetString = new DateTime(2000, 1, 2).ToString(); 28 | e.ToString().Should().Be(datetString + "\t172"); 29 | Console.WriteLine(e); 30 | } 31 | 32 | [TestMethod] 33 | public void EventCtorTest() 34 | { 35 | var t = new DateTime(2000, 1, 2); 36 | Event e = new Event(t, 72); 37 | e.Value.Should().Be(72); 38 | e.Time.Should().Be(t); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /TeaFiles.Test/Base/NameValueTest.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | using System; 3 | using System.Collections; 4 | using System.Globalization; 5 | using System.Linq; 6 | using System.Threading; 7 | using Microsoft.VisualStudio.TestTools.UnitTesting; 8 | using SharpTestsEx; 9 | 10 | namespace TeaTime 11 | { 12 | [TestClass] 13 | public class NameValueTest 14 | { 15 | [TestInitialize] 16 | public void Init() 17 | { 18 | // Ensure english error messages on non english systems. 19 | Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US"); 20 | } 21 | 22 | [TestMethod] 23 | public void ExoticCoverage() 24 | { 25 | Executing.This(() => new NameValue("astring", null)).Should().Throw(); 26 | Executing.This(() => new NameValue(null, 1)).Should().Throw(); 27 | Executing.This(() => new NameValue(null, 1.23)).Should().Throw(); 28 | Executing.This(() => new NameValue(null, "duffy duck")).Should().Throw(); 29 | Executing.This(() => new NameValue(null, Guid.Empty)).Should().Throw(); 30 | 31 | var nvc = new NameValueCollection(); 32 | IEnumerable enumerable = nvc; 33 | enumerable.GetEnumerator().Should().Not.Be.Null(); 34 | } 35 | 36 | [TestMethod] 37 | public void NameValueCollectionAdd() 38 | { 39 | NameValueCollection nvc = new NameValueCollection(); 40 | nvc.Add("anint", 17); 41 | nvc.Add("adouble", 1.234); 42 | nvc.Add("astring", "bugs bunny"); 43 | nvc.Add("aguid", Guid.Empty); 44 | 45 | nvc.Count.Should().Be(4); 46 | 47 | nvc.GetValue("anint").Should().Be(17); 48 | nvc.GetValue("adouble").Should().Be(1.234); 49 | nvc.GetValue("astring").Should().Be("bugs bunny"); 50 | nvc.GetValue("aguid").Should().Be(Guid.Empty); 51 | 52 | int i = 1; 53 | 10.Times(() => nvc.Add("x" + i, i++)); 54 | 55 | nvc.ToString().Should().Contain("anint"); 56 | nvc.ToString().Should().Contain("17"); 57 | nvc.ToString().Should().Contain("adouble"); 58 | nvc.ToString().Should().Contain("bugs bunny"); 59 | nvc.ToString().Should().Contain("aguid"); 60 | nvc.ToString().Should().Contain("..."); 61 | } 62 | 63 | [TestMethod] 64 | public void ValueTextTest() 65 | { 66 | string s = new NameValue("name", 1.23).ValueText; 67 | (s == "1.23" || s == "1,23").Should().Be.True(); // comma will be either . or , 68 | 69 | new NameValue("name", "bugs bunny").ValueText.Should().Be("bugs bunny"); 70 | new NameValue("name", Guid.Empty).ValueText.Should().Be(Guid.Empty.ToString()); 71 | } 72 | 73 | [TestMethod] 74 | public void NameValueCollectionFactoryTest() 75 | { 76 | var nvc = NameValueCollection.From("aname", 42, "bname", Guid.Empty, "cname", "cvalue", "dname", 12.34); 77 | nvc.Count.Should().Be(4); 78 | nvc.Select(nv => nv.Name).Should().Have.SameSequenceAs("aname", "bname", "cname", "dname"); 79 | nvc.Select(nv => nv.Kind).Should().Have.SameSequenceAs(NameValue.ValueKind.Int32, NameValue.ValueKind.Guid, NameValue.ValueKind.Text, NameValue.ValueKind.Double); 80 | nvc.Select(nv => nv.ValueText).Should().Have.SameSequenceAs("42", Guid.Empty.ToString(), "cvalue", 12.34.ToString()); 81 | } 82 | 83 | [TestMethod] 84 | public void NameValueCollectionFactoryExceptions() 85 | { 86 | Executing.This(() => NameValueCollection.From(111, 42)).Should().Throw(); 87 | Executing.This(() => NameValueCollection.From(111, 42, 11)).Should().Throw(); 88 | } 89 | 90 | [TestMethod] 91 | public void NameValueShouldNeverHaveNullValueTest() 92 | { 93 | Executing.This(() => new NameValue("name1", null)).Should().Throw(); 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /TeaFiles.Test/Base/TimeScaleTest.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | using System; 3 | using System.Globalization; 4 | using System.Threading; 5 | using Microsoft.VisualStudio.TestTools.UnitTesting; 6 | using SharpTestsEx; 7 | 8 | namespace TeaTime 9 | { 10 | [TestClass] 11 | public class TimeScaleTest 12 | { 13 | [TestInitialize] 14 | public void Init() 15 | { 16 | // Ensure english error messages on non english systems. 17 | Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US"); 18 | } 19 | 20 | [TestMethod] 21 | public void ExoticCoverage() 22 | { 23 | Timescale.Java.GetHashCode().Should().Not.Be.EqualTo(Timescale.Net.GetHashCode()); // otherwise our Hashcode generation would be questionable 24 | Executing.This(() => Timescale.FromEpoch(1, TimeSpan.TicksPerDay * 2)).Should().Throw(); 25 | Executing.This(() => Timescale.FromEpoch(1, 1, 1, TimeSpan.TicksPerDay * 2)).Should().Throw(); 26 | } 27 | 28 | [TestMethod] 29 | public void TickScaleTest1to1() 30 | { 31 | var scale = Timescale.FromEpoch(1, 1, 1, TimeSpan.TicksPerDay); 32 | scale.Epoch.Should().Be(0); 33 | scale.TicksPerDay.Should().Be(TimeSpan.TicksPerDay); 34 | scale.NetToScale(1234567).Should().Be(1234567); 35 | scale.ScaleToNet(1234567).Should().Be(1234567); 36 | } 37 | 38 | [TestMethod] 39 | public void TickScaleTest1to7() 40 | { 41 | var scale = Timescale.FromEpoch(1, 1, 1, TimeSpan.TicksPerDay / 7); 42 | scale.Epoch.Should().Be(0); 43 | scale.TicksPerDay.Should().Be(TimeSpan.TicksPerDay / 7); 44 | scale.NetToScale(1234567).Should().Be(1234567 / 7); 45 | scale.ScaleToNet(1234567).Should().Be(1234567 * 7); 46 | } 47 | 48 | [TestMethod] 49 | public void Equality() 50 | { 51 | (Timescale.Java == Timescale.Net).Should().Be.False(); 52 | (Timescale.Java != Timescale.Net).Should().Be.True(); 53 | Timescale.Java.Equals(711).Should().Be.False(); 54 | } 55 | 56 | [TestMethod] 57 | public void WellknownNames() 58 | { 59 | Timescale.Net.WellKnownName.Should().Be("Net"); 60 | Timescale.Java.WellKnownName.Should().Be("Java"); 61 | Timescale.FromEpoch(222, 33).WellKnownName.Should().Be("Custom"); 62 | } 63 | 64 | [TestMethod] 65 | public void ToStringTest() 66 | { 67 | Timescale.Net.ToString().Should().Be("Net"); 68 | Timescale.Java.ToString().Should().Be("Java"); 69 | Timescale.FromEpoch(1000, 1).ToString().Should().Be("1000,1"); 70 | } 71 | 72 | [TestMethod] 73 | public void UsingJavaTime() 74 | { 75 | Time.Scale = Timescale.Java; 76 | 77 | Time t = new Time(2012, 1, 2); // use Time the same way as you use DateTime 78 | DateTime dt = t; // implicit conversion 79 | 80 | // all logical values are the same 81 | t.Year.Should().Be(dt.Year); 82 | t.Month.Should().Be(dt.Month); 83 | t.Day.Should().Be(dt.Day); 84 | t.Hour.Should().Be(dt.Hour); 85 | t.Minute.Should().Be(dt.Minute); 86 | t.Second.Should().Be(dt.Second); 87 | 88 | // but the Tick representation is different 89 | t.Ticks.Should().Be.LessThan(dt.Ticks); // TeaTime.Time.Ticks < System.DateTime.Ticks 90 | } 91 | 92 | [TestMethod] 93 | public void UsingNetTime() 94 | { 95 | Time.Scale = Timescale.Net; // <- using System.DateTime Tick representation 96 | 97 | // as in the previous example 98 | Time t = new Time(2012, 1, 2); 99 | DateTime dt = t; 100 | t.Year.Should().Be(dt.Year); 101 | t.Month.Should().Be(dt.Month); 102 | t.Day.Should().Be(dt.Day); 103 | t.Hour.Should().Be(dt.Hour); 104 | t.Minute.Should().Be(dt.Minute); 105 | t.Second.Should().Be(dt.Second); 106 | 107 | // now, Ticks are equal! 108 | t.Ticks.Should().Be.EqualTo(dt.Ticks); // TeaTime.Time.Ticks == System.DateTime.Ticks 109 | } 110 | 111 | [TestMethod] 112 | public void JavaScaleValueTest() 113 | { 114 | Time.Scale = Timescale.Java; 115 | Time t = new Time(1970, 1, 3); 116 | t.Ticks.Should().Be(2 * 86400 * 1000); 117 | } 118 | 119 | [TestMethod] 120 | public void DateTimeToTimeConversionConsidersScale() 121 | { 122 | Time.Scale = Timescale.Java; 123 | Time t = new DateTime(1970, 1, 3); 124 | t.Ticks.Should().Be(2 * 86400 * 1000); 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /TeaFiles.Test/Description/DescriptionTest.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | using System.IO; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | using SharpTestsEx; 5 | 6 | namespace TeaTime 7 | { 8 | [TestClass] 9 | public class DescriptionTest 10 | { 11 | [TestMethod] 12 | public void DescriptionOfZeroFileIsNotNull() 13 | { 14 | var filename = "DescriptionTest_DescriptionOfZeroFileIsNotNull.tea"; 15 | using (TeaFile.Create(filename)) 16 | { 17 | } 18 | using (var tf = TeaFile.OpenRead(filename)) 19 | { 20 | tf.Description.Should().Not.Be.Null(); 21 | } 22 | } 23 | 24 | public struct StructA 25 | { 26 | public byte A; 27 | public double B; 28 | } 29 | 30 | [TestMethod] 31 | public void IsTimeSeriesFalse() 32 | { 33 | var stream = new MemoryStream(); 34 | using (var tf = TeaFile.Create(stream)) 35 | { 36 | var d = tf.Description; 37 | d.ItemDescription.HasEventTime.Should().Be.False(); 38 | } 39 | } 40 | 41 | [TestMethod] 42 | public void IsTimeSeriesTrue() 43 | { 44 | var stream = new MemoryStream(); 45 | using (var tf = TeaFile>.Create(stream)) 46 | { 47 | var d = tf.Description; 48 | d.ItemDescription.HasEventTime.Should().Be.True(); 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /TeaFiles.Test/Description/FieldTest.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | using System; 3 | using System.Linq; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | using SharpTestsEx; 6 | using TeaTime.SampleItems; 7 | 8 | namespace TeaTime 9 | { 10 | [TestClass] 11 | public class FieldTest 12 | { 13 | [TestMethod] 14 | public void ExoticCoverage() 15 | { 16 | var f = new Field(); 17 | Executing.This(() => f.GetValue(null)).Should().Throw(); 18 | } 19 | 20 | [TestMethod] 21 | public void GetValueTest() 22 | { 23 | var filename = "FieldTest_GetValueTest.tea"; 24 | using (var tf = TeaFile.Create(filename)) 25 | { 26 | tf.Write(Enumerable.Range(1, 10).Select(i => new OHLCV {Close = i * 101})); 27 | } 28 | using (var tf = TeaFile.OpenRead(filename)) 29 | { 30 | var id = tf.Description.ItemDescription; 31 | var field = id.Fields.First(ff => ff.Name == "Close"); 32 | 33 | Item item = tf.Items.First(); 34 | field.GetValue(item).Should().Be(101.0); 35 | 36 | item = tf.Items[3]; 37 | field.GetValue(item).Should().Be(404.0); 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /TeaFiles.Test/Description/ItemDescriptionDetailsTest.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | using System.Globalization; 3 | using System.Threading; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | using SharpTestsEx; 6 | 7 | namespace TeaTime 8 | { 9 | [TestClass] 10 | public class ItemDescriptionDetailsTest 11 | { 12 | [TestInitialize] 13 | public void Init() 14 | { 15 | // Ensure english error messages on non english systems. 16 | Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US"); 17 | } 18 | 19 | [TestMethod] 20 | public void Usage() 21 | { 22 | var allButNoName = ItemDescriptionElements.All & ~ItemDescriptionElements.ItemName; 23 | allButNoName.HasFlag(ItemDescriptionElements.ItemName).Should().Be.False(); 24 | allButNoName.HasFlag(ItemDescriptionElements.ItemSize).Should().Be.True(); 25 | allButNoName.HasFlag(ItemDescriptionElements.FieldOffsets).Should().Be.True(); 26 | allButNoName.HasFlag(ItemDescriptionElements.FieldNames).Should().Be.True(); 27 | allButNoName.HasFlag(ItemDescriptionElements.FieldTypes).Should().Be.True(); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /TeaFiles.Test/Description/TeaTypeTest.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | using System; 3 | using System.Globalization; 4 | using System.Threading; 5 | using Microsoft.VisualStudio.TestTools.UnitTesting; 6 | using SharpTestsEx; 7 | 8 | namespace TeaTime 9 | { 10 | [TestClass] 11 | public class TeaTypeTest 12 | { 13 | [TestInitialize] 14 | public void Init() 15 | { 16 | // Ensure english error messages on non english systems. 17 | Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US"); 18 | } 19 | 20 | [TestMethod] 21 | public void TeaTypeMapping() 22 | { 23 | typeof (SByte).GetFieldType().Should().Be(FieldType.Int8); 24 | typeof (Int16).GetFieldType().Should().Be(FieldType.Int16); 25 | typeof (Int32).GetFieldType().Should().Be(FieldType.Int32); 26 | typeof (Int64).GetFieldType().Should().Be(FieldType.Int64); 27 | 28 | typeof (Byte).GetFieldType().Should().Be(FieldType.UInt8); 29 | typeof (UInt16).GetFieldType().Should().Be(FieldType.UInt16); 30 | typeof (UInt32).GetFieldType().Should().Be(FieldType.UInt32); 31 | typeof (UInt64).GetFieldType().Should().Be(FieldType.UInt64); 32 | 33 | typeof (float).GetFieldType().Should().Be(FieldType.Float); 34 | typeof (double).GetFieldType().Should().Be(FieldType.Double); 35 | 36 | typeof (Time).GetFieldType().Should().Be(FieldType.Int64); 37 | 38 | typeof (decimal).GetFieldType().Should().Be(FieldType.NetDecimal); 39 | 40 | Executing.This(() => typeof (DateTime).GetFieldType()).Should().Throw(); 41 | Executing.This(() => typeof (string).GetFieldType()).Should().Throw(); 42 | } 43 | 44 | [TestMethod] 45 | public void TeaTypeSize() 46 | { 47 | typeof (SByte).GetFieldType().GetSize().Should().Be(sizeof (SByte)); 48 | typeof (Int16).GetFieldType().GetSize().Should().Be(sizeof (Int16)); 49 | typeof (Int32).GetFieldType().GetSize().Should().Be(sizeof (Int32)); 50 | typeof (Int64).GetFieldType().GetSize().Should().Be(sizeof (Int64)); 51 | 52 | typeof (Byte).GetFieldType().GetSize().Should().Be(sizeof (byte)); 53 | typeof (UInt16).GetFieldType().GetSize().Should().Be(sizeof (UInt16)); 54 | typeof (UInt32).GetFieldType().GetSize().Should().Be(sizeof (UInt32)); 55 | typeof (UInt64).GetFieldType().GetSize().Should().Be(sizeof (UInt64)); 56 | 57 | typeof (float).GetFieldType().GetSize().Should().Be(sizeof (float)); 58 | typeof (double).GetFieldType().GetSize().Should().Be(sizeof (double)); 59 | 60 | typeof (decimal).GetFieldType().GetSize().Should().Be(sizeof (decimal)); 61 | 62 | Executing.This(() => FieldType.None.GetSize()).Should().Throw(); 63 | Executing.This(() => ((FieldType)777).GetSize()).Should().Throw(); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /TeaFiles.Test/ExtensionsTest.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Reflection; 7 | using Microsoft.VisualStudio.TestTools.UnitTesting; 8 | using SharpTestsEx; 9 | 10 | namespace TeaTime 11 | { 12 | [TestClass] 13 | public class ExtensionsTest 14 | { 15 | [TestMethod] 16 | public void ToSafeStringTest() 17 | { 18 | 7.SafeToString("o is null").Should().Be(7.ToString()); 19 | 20 | object o = null; 21 | o.SafeToString("o is null").Should().Be("o is null"); 22 | } 23 | 24 | [TestMethod] 25 | public void ToSafeStringEmptyTest() 26 | { 27 | 7.SafeToString().Should().Be(7.ToString()); 28 | 29 | object o = null; 30 | o.SafeToString().Should().Be("empty"); 31 | } 32 | 33 | [TestMethod] 34 | public void IsDefinedTest() 35 | { 36 | MethodBase.GetCurrentMethod().IsDefined().Should().Be.True(); 37 | MethodBase.GetCurrentMethod().IsDefined().Should().Be.False(); 38 | } 39 | 40 | [TestMethod] 41 | public void TimesTest() 42 | { 43 | int n = 0; 44 | 0.Times(() => n++); 45 | n.Should().Be(0); 46 | 47 | 3.Times(() => n++); 48 | n.Should().Be(3); 49 | } 50 | 51 | struct Test 52 | { 53 | public string S; 54 | public int N; 55 | } 56 | 57 | [TestMethod] 58 | public void ToStringFromFieldsTest() 59 | { 60 | Test t = new Test {S = "s", N = 71}; 61 | t.ToStringFromFields("#").Should().Be("S=s#N=71"); 62 | } 63 | 64 | [TestMethod] 65 | public void ToStringFromFieldsDefaultTest() 66 | { 67 | Test t = new Test {S = "s", N = 71}; 68 | t.ToStringFromFields().Should().Be("S=s N=71"); 69 | } 70 | 71 | [TestMethod] 72 | public void FormattedTest() 73 | { 74 | "{0} => {1}".Formatted(42, 71).Should().Be("42 => 71"); 75 | } 76 | 77 | [TestMethod] 78 | public void JoinedTest() 79 | { 80 | var arr = new[] {2, 3, 5}; 81 | arr.Joined("-").Should().Be("2-3-5"); 82 | } 83 | 84 | [TestMethod] 85 | public void GetFirstPartTest() 86 | { 87 | "123456".GetFirstPart('4').Should().Be("123"); 88 | "123456".GetFirstPart('9').Should().Be("123456"); 89 | "".GetFirstPart('9').Should().Be(""); 90 | } 91 | 92 | [TestMethod] 93 | public void ForEachTest() 94 | { 95 | int i = 0; 96 | IEnumerable e = new[] {1.1, 2.2, 3.3}; 97 | var list = e.ToList(); 98 | e.ForEach((v) => v.Should().Be(list[i++])); 99 | } 100 | 101 | [TestMethod] 102 | public void ReadTeaType() 103 | { 104 | var ms = new MemoryStream(); 105 | var w = new BinaryWriter(ms); 106 | w.Write((byte)11); 107 | w.Write((UInt16)22); 108 | w.Write(33); 109 | w.Write((UInt64)44L); 110 | w.Write((sbyte)-11); 111 | w.Write((short)-22); 112 | w.Write(-33); 113 | w.Write(-44L); 114 | w.Write(7.22f); 115 | w.Write(1.23d); 116 | w.Write(12345m); 117 | 118 | ms.Position = 0; 119 | var r = new BinaryReader(ms); 120 | r.Read(FieldType.UInt8).Should().Be((byte)11); 121 | r.Read(FieldType.UInt16).Should().Be((UInt16)22u); 122 | r.Read(FieldType.UInt32).Should().Be(33u); 123 | r.Read(FieldType.UInt64).Should().Be(44Lu); 124 | 125 | r.Read(FieldType.Int8).Should().Be((sbyte)-11); 126 | r.Read(FieldType.Int16).Should().Be((Int16)(-22)); 127 | r.Read(FieldType.Int32).Should().Be(-33); 128 | r.Read(FieldType.Int64).Should().Be((-44L)); 129 | 130 | r.Read(FieldType.Float).Should().Be(7.22f); 131 | r.Read(FieldType.Double).Should().Be(1.23d); 132 | r.Read(FieldType.NetDecimal).Should().Be(12345m); 133 | 134 | Executing.This(() => r.Read((FieldType)777)).Should().Throw(); 135 | Executing.This(() => ((BinaryReader)null).Read(FieldType.UInt8)).Should().Throw(); 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /TeaFiles.Test/Header/HeaderManagerTest.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | using System; 3 | using System.Globalization; 4 | using System.IO; 5 | using System.Threading; 6 | using Microsoft.VisualStudio.TestTools.UnitTesting; 7 | using Moq; 8 | using SharpTestsEx; 9 | using TeaTime.IO; 10 | 11 | namespace TeaTime.Header 12 | { 13 | [TestClass] 14 | public class HeaderManagerTest 15 | { 16 | [TestInitialize] 17 | public void Init() 18 | { 19 | // get english error messages on non english systems. 20 | Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US"); 21 | } 22 | 23 | //[TestCleanup] 24 | //public void TestTearDown() 25 | //{ 26 | // HeaderManager.Instance.Initialized(); 27 | //} 28 | 29 | [TestMethod] 30 | public void WriteCoreHeader() 31 | { 32 | var ms = new MemoryStream(); 33 | HeaderManager.Instance.WriteHeader(new FormattedWriter(new FileIO(ms)), null); 34 | } 35 | 36 | [TestMethod] 37 | public void CreateSectionsTest() 38 | { 39 | var fw = new Mock(MockBehavior.Strict); 40 | var wc = new WriteContext(fw.Object); 41 | wc.ItemAreaStart.Should().Be(0); // not set yet 42 | HeaderManager.Instance.CreateSections(wc); 43 | wc.ItemAreaStart.Should().Be(32); 44 | } 45 | 46 | [TestMethod] 47 | public void CoreHeaderRoundTrip() 48 | { 49 | var ms = new MemoryStream(); 50 | HeaderManager.Instance.WriteHeader(new FormattedWriter(new FileIO(ms)), null); 51 | ms.Length.Should().Be(4 * 8); 52 | ms.Position = 0; 53 | var rc = HeaderManager.Instance.ReadHeader(new FormattedReader(new FileIO(ms))); 54 | rc.Should().Not.Be.Null(); 55 | rc.ItemAreaStart.Should().Be(32); 56 | rc.ItemAreaEnd.Should().Be(0); 57 | rc.SectionCount.Should().Be(0); 58 | ms.Position.Should().Be(ms.Length); 59 | } 60 | 61 | [TestMethod] 62 | public void HeaderHasLengthOfItemStart() 63 | { 64 | var ms = new MemoryStream(); 65 | var desc = new TeaFileDescription(); 66 | desc.ContentDescription = "a"; 67 | var wc = HeaderManager.Instance.WriteHeader(new FormattedWriter(new FileIO(ms)), desc); 68 | ms.Length.Should().Be(wc.ItemAreaStart); 69 | ms.Position.Should().Be(wc.ItemAreaStart); 70 | (ms.Position % 8).Should().Be(0); 71 | } 72 | 73 | [TestMethod] 74 | public void HeaderWithContentDescription() 75 | { 76 | var ms = new MemoryStream(); 77 | var desc = new TeaFileDescription(); 78 | desc.ContentDescription = "a"; 79 | var wc = HeaderManager.Instance.WriteHeader(new FormattedWriter(new FileIO(ms)), desc); 80 | ms.Length.Should().Be.GreaterThan(4 * 8); 81 | ms.Position.Should().Be(wc.ItemAreaStart); 82 | (ms.Position % 8).Should().Be(0); 83 | ms.Position = 0; 84 | var rc = HeaderManager.Instance.ReadHeader(new FormattedReader(new FileIO(ms))); 85 | rc.Should().Not.Be.Null(); 86 | rc.ItemAreaStart.Should().Be.GreaterThan(32); 87 | rc.ItemAreaEnd.Should().Be(0); 88 | rc.SectionCount.Should().Be(1); 89 | ms.Position.Should().Be(ms.Length); 90 | ms.Position.Should().Be(rc.ItemAreaStart); 91 | (ms.Position % 8).Should().Be(0); 92 | } 93 | 94 | [TestMethod] 95 | public void SignatureError() 96 | { 97 | var ms = new MemoryStream(); 98 | var bw = new BinaryWriter(ms); 99 | bw.Write(123L); 100 | ms.Position = 0; 101 | 102 | Executing.This(() => HeaderManager.Instance.ReadHeader(new FormattedReader(new FileIO(ms)))).Should().Throw() 103 | .Exception.Message.Should().Contain("Expected Signature not found"); 104 | } 105 | 106 | [TestMethod] 107 | public void NextSectionOffsetWrong() 108 | { 109 | var ms = new MemoryStream(); 110 | var bw = new BinaryWriter(ms); 111 | bw.Write(0x0d0e0a0402080500); 112 | bw.Write(32L); 113 | bw.Write(0L); 114 | bw.Write(1L); // 1 section 115 | bw.Write(0x80); 116 | bw.Write(11111); 117 | ms.Position = 0; 118 | 119 | Executing.This(() => HeaderManager.Instance.ReadHeader(new FormattedReader(new FileIO(ms)))).Should().Throw() 120 | .Exception.Message.Should().Contain("NextSectionOffset of section"); 121 | } 122 | 123 | [TestMethod] 124 | public void BadSection() 125 | { 126 | var badSectionFormatter = new Mock(MockBehavior.Strict); 127 | badSectionFormatter.Setup(sf => sf.Id).Returns(0x1000); 128 | badSectionFormatter.Setup(sf => sf.Read(It.IsAny())).Callback((ReadContext rc) => rc.Reader.SkipBytes(16)); 129 | HeaderManager hm = new HeaderManager(); 130 | hm.AddSectionFormatter(badSectionFormatter.Object); 131 | 132 | var ms = new MemoryStream(); 133 | var bw = new BinaryWriter(ms); 134 | bw.Write(0x0d0e0a0402080500); 135 | bw.Write(200L); 136 | bw.Write(0L); 137 | bw.Write(1L); // 1 section 138 | bw.Write(badSectionFormatter.Object.Id); 139 | bw.Write(2); 140 | 141 | 20.Times(() => bw.Write(0L)); 142 | 143 | ms.Position = 0; 144 | 145 | Executing.This(() => hm.ReadHeader(new FormattedReader(new FileIO(ms)))).Should().Throw() 146 | .Exception.Message.Should().Contain("Section read too many bytes"); 147 | } 148 | 149 | [TestMethod] 150 | public void GeneralExceptionWhileReadingHeader() 151 | { 152 | var stream = new Mock(MockBehavior.Strict); 153 | stream.Setup(s => s.CanRead).Returns(true); 154 | stream.Setup(s => s.Read(It.IsAny(), It.IsAny(), It.IsAny())).Throws(new Exception("testException")); 155 | 156 | Executing.This(() => HeaderManager.Instance.ReadHeader(new FormattedReader(new FileIO(stream.Object)))).Should().Throw() 157 | .Exception.Message.Should().Contain("Error reading TeaFile Header: testException"); 158 | } 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /TeaFiles.Test/Header/Sections/ContentSectionFormatterTest.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | using System; 3 | using System.Globalization; 4 | using System.IO; 5 | using System.Threading; 6 | using Microsoft.VisualStudio.TestTools.UnitTesting; 7 | using Moq; 8 | using SharpTestsEx; 9 | using TeaTime.IO; 10 | 11 | namespace TeaTime.Header.Sections 12 | { 13 | [TestClass] 14 | public class ContentSectionFormatterTest 15 | { 16 | [TestInitialize] 17 | public void Init() 18 | { 19 | // Ensure english error messages on non english systems. 20 | Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US"); 21 | } 22 | 23 | [TestMethod] 24 | public void ExoticCoverageTest() 25 | { 26 | ISectionFormatter f = new ContentSectionFormatter(); 27 | Executing.This(() => f.Write(null)).Should().Throw(); 28 | Executing.This(() => f.Read(null)).Should().Throw(); 29 | f.Id.Should().Be(0x80); 30 | } 31 | 32 | [TestMethod] 33 | public void ContentSectionFormatterEmptyDescription() 34 | { 35 | var fw = new Mock(MockBehavior.Strict); 36 | var wc = new WriteContext(fw.Object); 37 | ISectionFormatter f = new ContentSectionFormatter(); 38 | f.Write(wc); 39 | // due to the strict behavior of the fw mock, this test succeeds only if no method of fw was called. 40 | } 41 | 42 | [TestMethod] 43 | public void ContentSectionFormatterDescriptionSetButNoContentDescription() 44 | { 45 | var fw = new Mock(MockBehavior.Strict); 46 | var wc = new WriteContext(fw.Object); 47 | wc.Description = new TeaFileDescription(); 48 | ISectionFormatter f = new ContentSectionFormatter(); 49 | f.Write(wc); 50 | // due to the strict behavior of the fw mock, this test succeeds only if no method of fw was called. 51 | } 52 | 53 | [TestMethod] 54 | public void ContentSectionFormatterRoundTrip() 55 | { 56 | const string testValue = "Météo pour Paris, France. @€"; 57 | 58 | var ms = new MemoryStream(); 59 | var fio = new FileIO(ms); 60 | var fw = new FormattedWriter(fio); 61 | var wc = new WriteContext(fw); 62 | wc.Description = new TeaFileDescription(); 63 | wc.Description.ContentDescription = testValue; 64 | ISectionFormatter f = new ContentSectionFormatter(); 65 | f.Write(wc); 66 | ms.Position = 0; 67 | var fr = new FormattedReader(fio); 68 | var rc = new ReadContext(fr); 69 | f.Read(rc); 70 | rc.Description.Should().Not.Be.Null(); 71 | rc.Description.ContentDescription.Should().Be(testValue); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /TeaFiles.Test/Header/Sections/ItemSectionFormatterTest.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | using System.Globalization; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading; 6 | using Microsoft.VisualStudio.TestTools.UnitTesting; 7 | using SharpTestsEx; 8 | using TeaTime.IO; 9 | 10 | namespace TeaTime.Header.Sections 11 | { 12 | [TestClass] 13 | public class ItemSectionFormatterTest 14 | { 15 | [TestInitialize] 16 | public void Init() 17 | { 18 | // Ensure english error messages on non english systems. 19 | Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US"); 20 | } 21 | 22 | [TestMethod] 23 | public void ExoticCoverage() 24 | { 25 | ISectionFormatter f = new ItemSectionFormatter(); 26 | f.Id.Should().Be(0x0a); 27 | } 28 | 29 | public struct OHLCV 30 | { 31 | public double Open; 32 | public double High; 33 | public double Low; 34 | public double Close; 35 | public long Volume; 36 | } 37 | 38 | [TestMethod] 39 | public void ItemSectionRoundTrip() 40 | { 41 | var ms = new MemoryStream(); 42 | var fio = new FileIO(ms); 43 | var fw = new FormattedWriter(fio); 44 | var wc = new WriteContext(fw); 45 | wc.Description = new TeaFileDescription(); 46 | var writeID = 47 | wc.Description.ItemDescription = ItemDescription.FromAnalysis>(); 48 | ISectionFormatter f = new ItemSectionFormatter(); 49 | f.Write(wc); 50 | 51 | ms.Position = 0; 52 | 53 | var fr = new FormattedReader(fio); 54 | var rc = new ReadContext(fr); 55 | rc.Description.Should().Not.Be.Null(); 56 | f.Read(rc); 57 | rc.Description.Should().Not.Be.Null(); 58 | var id = rc.Description.ItemDescription; 59 | 60 | id.ItemTypeName.Should().Be(typeof (Event).GetLanguageName()); 61 | id.ItemSize.Should().Be(wc.Description.ItemDescription.ItemSize); 62 | id.Fields.Select(ff => ff.Name).Should().Have.SameValuesAs("Time", "Open", "High", "Low", "Close", "Volume"); 63 | id.Fields.Select(ff => ff.Index).Should().Have.SameValuesAs(0, 1, 2, 3, 4, 5); 64 | id.Fields.Select(ff => ff.FieldType).Should().Have.SameValuesAs(FieldType.Int64, FieldType.Double, FieldType.Double, FieldType.Double, FieldType.Double); 65 | id.Fields.Select(ff => ff.Offset).Should().Have.SameValuesAs(writeID.Fields.Select(ff => ff.Offset)); 66 | 67 | ms.Position.Should().Be(ms.Length); // very important, all bytes must have been read 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /TeaFiles.Test/Header/Sections/NameValueSectionFormatterTest.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | using System; 3 | using System.Globalization; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Threading; 7 | using Microsoft.VisualStudio.TestTools.UnitTesting; 8 | using Moq; 9 | using SharpTestsEx; 10 | using TeaTime.IO; 11 | 12 | namespace TeaTime.Header.Sections 13 | { 14 | [TestClass] 15 | public class NameValueSectionFormatterTest 16 | { 17 | [TestInitialize] 18 | public void Init() 19 | { 20 | // Ensure english error messages on non english systems. 21 | Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US"); 22 | } 23 | 24 | [TestMethod] 25 | public void ExoticCoverageTest() 26 | { 27 | ISectionFormatter f = new NameValueSectionFormatter(); 28 | Executing.This(() => f.Write(null)).Should().Throw(); 29 | Executing.This(() => f.Read(null)).Should().Throw(); 30 | f.Id.Should().Be(0x81); 31 | } 32 | 33 | [TestMethod] 34 | public void NoNameValueSectionTest() 35 | { 36 | var fw = new Mock(MockBehavior.Strict); 37 | var wc = new WriteContext(fw.Object); 38 | 39 | ISectionFormatter f = new NameValueSectionFormatter(); 40 | f.Write(wc); 41 | 42 | wc.Description = new TeaFileDescription(); 43 | f.Write(wc); 44 | 45 | wc.Description.NameValues = new NameValueCollection(); 46 | f.Write(wc); 47 | 48 | // due to the strict behavior of the fw mock, this test succeeds only if no method of fw was called. 49 | } 50 | 51 | [TestMethod] 52 | public void NameValueSectionIOTest() 53 | { 54 | var fw = new Mock(MockBehavior.Strict); 55 | fw.Setup(w => w.WriteInt32(1)); 56 | fw.Setup(w => w.WriteNameValue(It.Is(nv => nv.Name == "someName"))); 57 | var wc = new WriteContext(fw.Object); 58 | 59 | ISectionFormatter f = new NameValueSectionFormatter(); 60 | wc.Description = new TeaFileDescription(); 61 | wc.Description.NameValues = new NameValueCollection(); 62 | wc.Description.NameValues.Add(new NameValue("someName", 1.23)); 63 | f.Write(wc); 64 | 65 | fw.Verify(); 66 | } 67 | 68 | [TestMethod] 69 | public void NameValueSectionRoundTrip1EntryTest() 70 | { 71 | var ms = new MemoryStream(); 72 | var fio = new FileIO(ms); 73 | var fw = new FormattedWriter(fio); 74 | var wc = new WriteContext(fw); 75 | 76 | ISectionFormatter f = new NameValueSectionFormatter(); 77 | wc.Description = new TeaFileDescription(); 78 | wc.Description.NameValues = new NameValueCollection(); 79 | wc.Description.NameValues.Add(new NameValue("someName", 1.23)); 80 | f.Write(wc); 81 | 82 | ms.Position = 0; 83 | 84 | var fr = new FormattedReader(fio); 85 | var rc = new ReadContext(fr); 86 | f.Read(rc); 87 | 88 | rc.Description.Should().Not.Be.Null(); 89 | rc.Description.NameValues.Should().Not.Be.Null(); 90 | rc.Description.NameValues.Should().Have.Count.EqualTo(1); 91 | rc.Description.NameValues.First().Name.Should().Be("someName"); 92 | rc.Description.NameValues.First().GetValue().Should().Be(1.23); 93 | } 94 | 95 | [TestMethod] 96 | public void NameValueSectionRoundTrip3EntriesTest() 97 | { 98 | var ms = new MemoryStream(); 99 | var fio = new FileIO(ms); 100 | var fw = new FormattedWriter(fio); 101 | var wc = new WriteContext(fw); 102 | 103 | ISectionFormatter f = new NameValueSectionFormatter(); 104 | wc.Description = new TeaFileDescription(); 105 | wc.Description.NameValues = new NameValueCollection(); 106 | wc.Description.NameValues.Add(new NameValue("someName", 1.23)); 107 | wc.Description.NameValues.Add(new NameValue("someName2", "second value")); 108 | wc.Description.NameValues.Add(new NameValue("someName3", 333)); 109 | f.Write(wc); 110 | 111 | ms.Position = 0; 112 | 113 | var fr = new FormattedReader(fio); 114 | var rc = new ReadContext(fr); 115 | f.Read(rc); 116 | 117 | rc.Description.Should().Not.Be.Null(); 118 | rc.Description.NameValues.Should().Not.Be.Null(); 119 | rc.Description.NameValues.Should().Have.Count.EqualTo(3); 120 | rc.Description.NameValues.Select(nv => nv.Name).Should().Have.SameSequenceAs("someName", "someName2", "someName3"); 121 | rc.Description.NameValues.Select(nv => nv.GetValue()).Should().Have.SameSequenceAs(1.23, "second value", 333); 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /TeaFiles.Test/IO/FormattedReaderTest.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | using System; 3 | using System.Globalization; 4 | using System.Text; 5 | using System.Threading; 6 | using Microsoft.VisualStudio.TestTools.UnitTesting; 7 | using Moq; 8 | using SharpTestsEx; 9 | 10 | namespace TeaTime.IO 11 | { 12 | [TestClass] 13 | public class FormattedReaderTest 14 | { 15 | [TestInitialize] 16 | public void Init() 17 | { 18 | // Ensure english error messages on non english systems. 19 | Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US"); 20 | } 21 | 22 | [TestMethod] 23 | public void FormattedReaderExoticCoverageTest() 24 | { 25 | var fio = new Mock(); 26 | var r = new FormattedReader(fio.Object); 27 | fio.Setup(io => io.ReadBytes(0)); // will cause name to be the empty string 28 | fio.Setup(io => io.ReadInt32()).Returns((int)NameValue.ValueKind.Invalid); 29 | Executing.This(() => r.ReadNameValue()).Should().Throw(); 30 | } 31 | 32 | [TestMethod] 33 | public void CtorTest() 34 | { 35 | var fio = new Mock(); 36 | new FormattedReader(fio.Object); 37 | } 38 | 39 | [TestMethod] 40 | public void ReadInt32Test() 41 | { 42 | var fio = new Mock(MockBehavior.Strict); 43 | fio.Setup(io => io.ReadInt32()).Returns(0x7fcc1111); 44 | var r = new FormattedReader(fio.Object); 45 | 46 | r.ReadInt32().Should().Be(0x7fcc1111); 47 | 48 | fio.Verify(f => f.ReadInt32(), Times.Once()); 49 | } 50 | 51 | [TestMethod] 52 | public void ReadInt64Test() 53 | { 54 | var fio = new Mock(MockBehavior.Strict); 55 | fio.Setup(io => io.ReadInt64()).Returns(0x7fcc111122223333); 56 | var r = new FormattedReader(fio.Object); 57 | 58 | r.ReadInt64().Should().Be(0x7fcc111122223333); 59 | 60 | fio.Verify(f => f.ReadInt64(), Times.Once()); 61 | } 62 | 63 | [TestMethod] 64 | public void ReadDoubleTest() 65 | { 66 | var fio = new Mock(MockBehavior.Strict); 67 | fio.Setup(io => io.ReadDouble()).Returns(1.2345); 68 | var r = new FormattedReader(fio.Object); 69 | 70 | r.ReadDouble().Should().Be(1.2345); 71 | 72 | fio.Verify(f => f.ReadDouble(), Times.Once()); 73 | } 74 | 75 | // ReadGuid was changed to private, since its intentional use is inside header reading only 76 | //[TestMethod] 77 | //public void ReadGuidTest() 78 | //{ 79 | // Guid g = new Guid("1976C195-8975-4E73-B777-23E1C548BC71"); 80 | // var fio = new Mock(MockBehavior.Strict); 81 | // fio.Setup(io => io.ReadBytes(16)).Returns(g.ToByteArray()); 82 | // var r = new FormattedReader(fio.Object); 83 | 84 | // r.ReadGuid().Should().Be(g); 85 | 86 | // fio.Verify(f => f.ReadBytes(16), Times.Once()); 87 | //} 88 | 89 | // ReadBytes was moved to FileIO 90 | //[TestMethod] 91 | //public void ReadBytesTest() 92 | //{ 93 | // var fio = new Mock(MockBehavior.Strict); 94 | // fio.Setup(io => io.ReadInt32()).Returns(3); 95 | // fio.Setup(io => io.ReadBytes(3)).Returns(new byte[]{7,4,11}); 96 | // var r = new FormattedReader(fio.Object); 97 | 98 | // r.ReadBytes().Should().Have.SameSequenceAs(new byte[]{7,4,11}); 99 | 100 | // fio.Verify(f => f.ReadInt32(), Times.Once()); 101 | // fio.Verify(f => f.ReadBytes(3), Times.Once()); 102 | //} 103 | 104 | //[TestMethod] 105 | //public void ReadEmptyBytesTest() 106 | //{ 107 | // var fio = new Mock(MockBehavior.Strict); 108 | // fio.Setup(io => io.ReadInt32()).Returns(0); 109 | // var r = new FormattedReader(fio.Object); 110 | 111 | // var bytes = r.ReadBytes(); 112 | 113 | // bytes.Should().Not.Be.Null(); 114 | // bytes.Length.Should().Be(0); 115 | // fio.Verify(f => f.ReadInt32(), Times.Once()); 116 | //} 117 | 118 | [TestMethod] 119 | public void ReadTextTest() 120 | { 121 | const string text = "Ce plat est originaire de la Grèce antique, de l'époque de la fondation de Marseille (Massalia) au VIIe siècle av. €"; 122 | var encoding = new UTF8Encoding(false, true); 123 | var bytes = encoding.GetBytes(text); 124 | var fio = new Mock(MockBehavior.Strict); 125 | fio.Setup(io => io.ReadInt32()).Returns(bytes.Length); 126 | fio.Setup(io => io.ReadBytes(bytes.Length)).Returns(bytes); 127 | var r = new FormattedReader(fio.Object); 128 | 129 | r.ReadText().Should().Be(text); 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /TeaFiles.Test/ItemsTTest.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | using System; 3 | using System.Collections.Generic; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | using TeaTime.SampleItems; 6 | 7 | namespace TeaTime 8 | { 9 | [TestClass] 10 | public class ItemTTest 11 | { 12 | [TestMethod] 13 | public void ItemsT_property_returns_value() 14 | { 15 | using (TeaFile f = TeaFile.Create(Guid.NewGuid() + "lab1.tea")) 16 | { 17 | var items = f.Items; 18 | Assert.IsNotNull(items); 19 | } 20 | } 21 | 22 | [TestMethod] 23 | public void ItemsT_property_has_count_0() 24 | { 25 | using (TeaFile f = TeaFile.Create(Guid.NewGuid() + "lab1.tea")) 26 | { 27 | var items = f.Items; 28 | Assert.AreEqual(0, items.Count); 29 | } 30 | } 31 | 32 | [TestMethod] 33 | public void ItemsT_property_has_count_1() 34 | { 35 | using (TeaFile f = TeaFile.Create(Guid.NewGuid() + "lab1.tea")) 36 | { 37 | f.Write(new OHLCV {Open = 111}); 38 | var items = f.Items; 39 | Assert.AreEqual(1, items.Count); 40 | } 41 | } 42 | 43 | [TestMethod] 44 | public void ItemsT_property_has_count_4() 45 | { 46 | using (TeaFile f = TeaFile.Create(Guid.NewGuid() + "lab1.tea")) 47 | { 48 | 4.Times(() => f.Write(new OHLCV {Open = 111})); 49 | var items = f.Items; 50 | Assert.AreEqual(4, items.Count); 51 | } 52 | } 53 | 54 | //[TestMethod] 55 | //public void ItemsT_FirstItem_and_LastItem_of_empty_file_have_struct_default_value() 56 | //{ 57 | // using (TeaFile f = TeaFile.Create("lab1.tea")) 58 | // { 59 | // var items = f.Items; 60 | // Assert.AreEqual(default(OHLCV), items.First); 61 | // Assert.AreEqual(default(OHLCV), items.Last); 62 | // } 63 | //} 64 | 65 | //[TestMethod] 66 | //public void ItemsT_FirstItem_and_LastItem_have_value_of_single_value() 67 | //{ 68 | // using (TeaFile f = TeaFile.Create("lab1.tea")) 69 | // { 70 | // var firstValue = new OHLCV { Open = 111 }; 71 | // f.Write(firstValue); 72 | // var items = f.Items; 73 | // Assert.AreEqual(items.First, items.First); 74 | // Assert.AreEqual(items.First, items.Last, "Since there is only one value, first and last must be the same value"); 75 | // } 76 | //} 77 | 78 | //[TestMethod] 79 | //public void ItemsT_FirstItem_and_LastItem_have_correct_values() 80 | //{ 81 | // using (TeaFile f = TeaFile.Create("lab1.tea")) 82 | // { 83 | // var firstValue = new OHLCV { Open = 111 }; 84 | // var someValue = new OHLCV { Open = 222 }; 85 | // var lastValue = new OHLCV { Open = 333 }; 86 | // f.Write(firstValue); 87 | // f.Write(someValue); 88 | // f.Write(someValue); 89 | // f.Write(someValue); 90 | // f.Write(lastValue); 91 | // var items = f.Items; 92 | // Assert.AreEqual(firstValue, items.First); 93 | // Assert.AreEqual(lastValue, items.Last); 94 | // } 95 | //} 96 | 97 | [TestMethod] 98 | public void ItemsT_Enumerating_Returns_Correct_Values() 99 | { 100 | using (TeaFile f = TeaFile.Create(Guid.NewGuid() + "lab1.tea")) 101 | { 102 | var firstValue = new OHLCV {Open = 111}; 103 | var someValue = new OHLCV {Open = 222}; 104 | var lastValue = new OHLCV {Open = 333}; 105 | 106 | List values = new List(); 107 | values.Add(firstValue); 108 | values.Add(someValue); 109 | values.Add(lastValue); 110 | 111 | int i = 0; 112 | foreach (OHLCV ohlcv in f.Items) 113 | { 114 | Assert.AreEqual(values[i], ohlcv); 115 | } 116 | } 117 | } 118 | 119 | [TestMethod] 120 | public void ItemsT_Enumerating_No_Enumeration_On_Empty_Collection() 121 | { 122 | using (TeaFile f = TeaFile.Create(Guid.NewGuid() + "lab1.tea")) 123 | { 124 | foreach (OHLCV ohlcv in f.Items) 125 | { 126 | Assert.Fail("no values are available, so no iteration should occur"); 127 | } 128 | } 129 | } 130 | 131 | [TestMethod] 132 | public void ItemsT_CountProperty_reflects_actual_number_of_items() 133 | { 134 | string filename = Guid.NewGuid() + "lab1.tea"; 135 | using (TeaFile f = TeaFile.Create(filename)) 136 | { 137 | Assert.AreEqual(0, f.Items.Count); 138 | f.Write(new OHLCV {Open = 111}); 139 | Assert.AreEqual(1, f.Items.Count, "After writing an item, Count is 1"); 140 | } 141 | 142 | using (var f = TeaFile.OpenRead(filename)) 143 | { 144 | Assert.AreEqual(1, f.Items.Count); 145 | } 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /TeaFiles.Test/Layout/ByteSearcherTest.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | using System.Globalization; 3 | using System.Threading; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | using SharpTestsEx; 6 | 7 | namespace TeaTime.Layout 8 | { 9 | [TestClass] 10 | public unsafe class ByteSearcherTest 11 | { 12 | [TestInitialize] 13 | public void Init() 14 | { 15 | // Ensure english error messages on non english systems. 16 | Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US"); 17 | } 18 | 19 | bool Match0(byte[] searchSpace, byte[] searchPattern) 20 | { 21 | fixed (byte* b = searchSpace) 22 | fixed (byte* s = searchPattern) 23 | { 24 | return ByteSearcher.StartsWith(b, s, searchPattern.Length); 25 | } 26 | } 27 | 28 | [TestMethod] 29 | public void MatchesTest() 30 | { 31 | this.Match0(new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, new byte[] {4}).Should().Be.False(); 32 | this.Match0(new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, new byte[] {1}).Should().Be.True(); 33 | this.Match0(new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, new byte[] {0}).Should().Be.False(); 34 | this.Match0(new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, new byte[] {4, 6}).Should().Be.False(); 35 | this.Match0(new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, new byte[] {7, 8, 9, 0}).Should().Be.False(); 36 | this.Match0(new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, new byte[] {1, 2, 3, 4, 5}).Should().Be.True(); 37 | } 38 | 39 | int MatchN(byte[] searchSpace, byte[] searchPattern) 40 | { 41 | fixed (byte* b = searchSpace) 42 | fixed (byte* s = searchPattern) 43 | { 44 | return ByteSearcher.GetPosition(b, s, searchSpace.Length, searchPattern.Length); 45 | } 46 | } 47 | 48 | [TestMethod] 49 | public void MatchNTest() 50 | { 51 | this.MatchN(new byte[] {1, 2}, new byte[] {2}).Should().Be(1); 52 | this.MatchN(new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, new byte[] {4}).Should().Be(3); 53 | this.MatchN(new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, new byte[] {1}).Should().Be(0); 54 | this.MatchN(new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, new byte[] {0}).Should().Be(9); 55 | Executing.This(() => this.MatchN(new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, new byte[] {4, 6})).Should().Throw(); 56 | this.MatchN(new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, new byte[] {7, 8, 9, 0}).Should().Be(6); 57 | this.MatchN(new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, new byte[] {1, 2, 3, 4, 5}).Should().Be(0); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /TeaFiles.Test/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | using System; 3 | using System.Reflection; 4 | using System.Runtime.InteropServices; 5 | 6 | [assembly: AssemblyTitle("TeaFiles.Net Test")] 7 | [assembly: AssemblyDescription("Unit tests for TeaFile.dll")] 8 | [assembly: AssemblyCompany("DiscreteLogics")] 9 | [assembly: AssemblyProduct("TeaTime - TeaFile")] 10 | [assembly: AssemblyCopyright("Copyright © DiscreteLogics 2012")] 11 | [assembly: AssemblyTrademark("TeaTime - TeaFile")] 12 | 13 | [assembly: CLSCompliant(false)] 14 | [assembly: ComVisible(false)] 15 | -------------------------------------------------------------------------------- /TeaFiles.Test/SampleItems/ItemTypes.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | using System.Runtime.InteropServices; 3 | 4 | namespace TeaTime.SampleItems 5 | { 6 | public struct OHLCV 7 | { 8 | public double Open; 9 | public double High; 10 | public double Low; 11 | public double Close; 12 | public int Volume; 13 | 14 | public override string ToString() 15 | { 16 | return string.Format("{0} {1} {2} {3} {4}", this.Open, this.High, this.Low, this.Close, this.Volume); 17 | } 18 | } 19 | 20 | [StructLayout(LayoutKind.Sequential)] 21 | struct TOHLCV 22 | { 23 | public Time Time; 24 | public double Open; 25 | public double High; 26 | public double Low; 27 | public double Close; 28 | 29 | public int Volume; 30 | 31 | public override string ToString() 32 | { 33 | return this.Time.ToString(); 34 | } 35 | } 36 | 37 | [StructLayout(LayoutKind.Explicit, Size = 40)] 38 | struct OHLCV3 39 | { 40 | [FieldOffset(0)] 41 | public double Open; 42 | 43 | [FieldOffset(8)] 44 | public double High; 45 | 46 | [FieldOffset(16)] 47 | public double Low; 48 | 49 | [FieldOffset(24)] 50 | public double Close; 51 | 52 | [EventTime] 53 | [FieldOffset(32)] 54 | public Time Time; 55 | } 56 | 57 | public struct DecimalTick 58 | { 59 | public decimal Price; 60 | public int Volume; 61 | public int Id; 62 | 63 | public override string ToString() 64 | { 65 | return this.Id + " " + this.Price + " " + this.Volume; 66 | } 67 | } 68 | 69 | public struct Tick 70 | { 71 | public Time Time; 72 | public double Price; 73 | public int Volume; 74 | public int Id; 75 | } 76 | 77 | public struct EmptyStruct 78 | { 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /TeaFiles.Test/SnapshotTest.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | 3 | #if false 4 | 5 | // copyright discretelogics © 2011 6 | using System; 7 | using System.IO; 8 | using System.Linq; 9 | using TeaTime; 10 | using Microsoft.VisualStudio.TestTools.UnitTesting; 11 | using SharpTestsEx; 12 | 13 | namespace DiscreteLogics.TeaFile.Tests 14 | { 15 | [TestClass] 16 | public class SnapshotTest 17 | { 18 | [TestMethod] 19 | public void SnapshotReturnsCorrectValue() 20 | { 21 | const string filename = "snapshotreturnscorrectvalue.tea"; 22 | using (var tf = TeaFile.Create(filename)) 23 | { 24 | tf.Write(Enumerable.Range(3, 5)); 25 | } 26 | 27 | var s = TeaTime.TeaFile.GetSnapshot(filename); 28 | 29 | s.First.Values[0].Should().Be(3); 30 | s.Last.Values[0].Should().Be(7); 31 | s.Count.Should().Be(5); 32 | } 33 | } 34 | } 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /TeaFiles.Test/TeaFileCoreTest.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | using System; 3 | using System.Globalization; 4 | using System.IO; 5 | using System.Threading; 6 | using Microsoft.VisualStudio.TestTools.UnitTesting; 7 | using SharpTestsEx; 8 | 9 | namespace TeaTime 10 | { 11 | [TestClass] 12 | public class TeaFileCoreTest 13 | { 14 | [TestInitialize] 15 | public void Init() 16 | { 17 | // Ensure english error messages on non english systems. 18 | Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US"); 19 | } 20 | 21 | [TestMethod] 22 | public void ExoticCoverage() 23 | { 24 | Stream stream = null; 25 | var tfc = new TeaFileCore(stream, true); 26 | tfc.Description = null; 27 | Executing.This(() => tfc.IsAccessibleWith(null, ItemDescriptionElements.All)).Should().NotThrow(); 28 | Executing.This(() => tfc.ItemDescriptionExists()).Should().Throw(); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /TeaFiles.Test/TestStream.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | using System; 3 | using System.IO; 4 | using System.Linq; 5 | 6 | namespace TeaTime 7 | { 8 | class TestStream : MemoryStream 9 | { 10 | public bool FlushWasCalled { get; set; } 11 | public bool WasDisposed { get; set; } 12 | public long FailAfterPosition { get; set; } 13 | public long StopReadAfterPosition { get; set; } 14 | 15 | public TestStream() 16 | { 17 | this.FailAfterPosition = long.MaxValue; 18 | this.StopReadAfterPosition = long.MaxValue; 19 | } 20 | 21 | public override void Write(byte[] buffer, int offset, int count) 22 | { 23 | Console.WriteLine("[Write({0}] o={1}, n={2})".Formatted(buffer.Take(count).Joined(","), offset, count)); 24 | base.Write(buffer, offset, count); 25 | if (this.Position >= this.FailAfterPosition) 26 | { 27 | throw new IOException("lets say the hard disc failed."); 28 | } 29 | } 30 | 31 | public override int Read(byte[] buffer, int offset, int count) 32 | { 33 | if (this.Position + count > this.StopReadAfterPosition) 34 | { 35 | count = (int)(this.StopReadAfterPosition - this.Position); 36 | if (count < 0) count = 0; 37 | } 38 | var n = base.Read(buffer, offset, count); 39 | if (this.Position >= this.FailAfterPosition) 40 | { 41 | throw new IOException("lets say the hard disc failed."); 42 | } 43 | return n; 44 | } 45 | 46 | protected override void Dispose(bool disposing) 47 | { 48 | Console.WriteLine("Dispose"); 49 | this.WasDisposed = true; 50 | base.Dispose(disposing); 51 | } 52 | 53 | public override void Flush() 54 | { 55 | Console.WriteLine("Flush"); 56 | this.FlushWasCalled = true; 57 | base.Flush(); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /TeaFiles.Test/TestUtils.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | using System.IO; 3 | 4 | namespace TeaTime 5 | { 6 | class TestUtils 7 | { 8 | public static bool IsLocked(string filename) 9 | { 10 | try 11 | { 12 | var movedfilename = filename + "moved"; 13 | File.Move(filename, movedfilename); 14 | File.Move(movedfilename, filename); 15 | return false; 16 | } 17 | catch (IOException) 18 | { 19 | return true; 20 | } 21 | } 22 | 23 | public static Stream GetTeaFileEventInt7Values() 24 | { 25 | var stream = new MemoryStream(); 26 | using (var tf = TeaFile>.Create(stream)) 27 | { 28 | Time t = new Time(2000, 1, 1); 29 | for (int i = 0; i < 7; i++) 30 | { 31 | tf.Write(new Event {Time = t, Value = i * 1100}); 32 | t = new Time(t.Ticks + Time.Scale.TicksPerDay); // add methods for Time are not available (yet?) 33 | } 34 | } 35 | stream.Position = 0; 36 | return stream; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /TeaFiles.Test/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /TeaFiles/Access/Extensions.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | using System; 3 | using System.IO; 4 | 5 | namespace TeaTime 6 | { 7 | partial class Extensions 8 | { 9 | public static object Read(this BinaryReader reader, FieldType fieldType) 10 | { 11 | if (reader == null) throw new ArgumentNullException("reader"); 12 | switch (fieldType) 13 | { 14 | case FieldType.UInt8: 15 | return reader.ReadByte(); 16 | case FieldType.UInt16: 17 | return reader.ReadUInt16(); 18 | case FieldType.UInt32: 19 | return reader.ReadUInt32(); 20 | case FieldType.UInt64: 21 | return reader.ReadUInt64(); 22 | 23 | case FieldType.Int8: 24 | return reader.ReadSByte(); 25 | case FieldType.Int16: 26 | return reader.ReadInt16(); 27 | case FieldType.Int32: 28 | return reader.ReadInt32(); 29 | case FieldType.Int64: 30 | return reader.ReadInt64(); 31 | 32 | case FieldType.Float: 33 | return reader.ReadSingle(); 34 | case FieldType.Double: 35 | return reader.ReadDouble(); 36 | 37 | case FieldType.NetDecimal: 38 | return reader.ReadDecimal(); 39 | 40 | default: 41 | throw new ArgumentOutOfRangeException("Reading FieldType '{0}' from the stream failed, the type is not supported.".Formatted(fieldType)); 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /TeaFiles/Access/ManagedMemoryMapping.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | using System; 3 | using System.IO; 4 | using System.IO.MemoryMappedFiles; 5 | 6 | namespace TeaTime 7 | { 8 | /// 9 | /// Access items in TeaFiles via safe memory mapping using . 10 | /// 11 | /// 12 | /// This safe way of memory mapping incurs significant overhead such that performance 13 | /// gains are far behind using unsafe P/Invoke Memory Mapping.

14 | /// 15 | /// If pure managed code is required, this class might be prefered over . It also 16 | /// provides a type safe interface, while provides raw byte* only. For 17 | /// performance reasons however, MemoryMappedTeaFile is much faster. 18 | ///
19 | public sealed class ManagedMemoryMapping : IDisposable where T : struct 20 | { 21 | readonly int itemSize; 22 | readonly MemoryMappedFile memoryMappedFile; 23 | readonly MemoryMappedViewAccessor accessor; 24 | 25 | internal ManagedMemoryMapping(string path, long itemAreaStart, long itemAreaLength, int itemSize) 26 | { 27 | this.itemSize = itemSize; 28 | var fi = new FileInfo(path); 29 | this.memoryMappedFile = MemoryMappedFile.CreateFromFile(path, FileMode.Open, path, fi.Length, MemoryMappedFileAccess.Read); 30 | this.accessor = this.memoryMappedFile.CreateViewAccessor(itemAreaStart, itemAreaLength, MemoryMappedFileAccess.Read); 31 | } 32 | 33 | /// Reads an item at a given position. 34 | /// The item index to read. 35 | /// The item at index . 36 | public T Read(long itemIndex) 37 | { 38 | // hopefully the compiler removes some copy operations here 39 | T item; 40 | this.accessor.Read(itemIndex * this.itemSize, out item); 41 | return item; 42 | } 43 | 44 | /// Indexer to get items within this collection using array index syntax. 45 | /// The indexed item. 46 | public T this[int itemIndex] 47 | { 48 | get { return this.Read(itemIndex); } 49 | } 50 | 51 | #region Implementation of IDisposable 52 | 53 | /// Releases all resources. 54 | public void Dispose() 55 | { 56 | this.accessor.Dispose(); 57 | this.memoryMappedFile.Dispose(); 58 | } 59 | 60 | #endregion 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /TeaFiles/Access/Typed/ItemsCollectionT.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | 5 | namespace TeaTime 6 | { 7 | /// Provides access to items using typed . 8 | /// 9 | /// This interface adds direct access and count to . 10 | /// 11 | /// The item type. 12 | public interface IItemCollection : IEnumerable where T : struct 13 | { 14 | /// 15 | /// The number of items in the file. 16 | /// 17 | /// The number of items in the file. 18 | long Count { get; } 19 | 20 | /// Indexer to get items within this collection using array index syntax. 21 | /// The indexed item. 22 | T this[long index] { get; } 23 | } 24 | 25 | /// 26 | /// A TeaFile holds a collection of items. In instance of this class exposes access to this collection. 27 | /// 28 | /// 29 | /// Access is provided by an enumerator. In addition, the number of items in the file 30 | /// is exposed via its property.

31 | /// Usage:

32 | /// 33 | /// using(var teaFile = TeaFile<OHLCV>.OpenRead("ohlcv.tea")) 34 | /// { 35 | /// foreach(OHLCV item in teaFile.Items) 36 | /// { 37 | /// Console.WriteLine(item.Time); 38 | /// Console.WriteLine(item.Open); 39 | /// } 40 | /// } 41 | /// 42 | ///
43 | class ItemCollection : IItemCollection where T : struct 44 | { 45 | #region State 46 | 47 | internal readonly TeaFile teaFile; 48 | 49 | #endregion 50 | 51 | #region Construction & Initialization 52 | 53 | /// 54 | /// Contructs an instance, initializing it with a reference to the TeaFile it provides access to. 55 | /// 56 | /// 57 | /// This class shall be instantiated by an instance of a TeaFile only, thus it is internal. 58 | /// 59 | /// The tea file. 60 | internal ItemCollection(TeaFile teaFile) 61 | { 62 | this.teaFile = teaFile; 63 | } 64 | 65 | #endregion 66 | 67 | #region Public Interface 68 | 69 | /// 70 | /// The number of items in the file. 71 | /// 72 | /// The number of items in the file. 73 | public long Count 74 | { 75 | get { return this.teaFile.Count; } 76 | } 77 | 78 | /// Indexer to get items within this collection using array index syntax. 79 | /// The indexed item. 80 | public T this[long index] 81 | { 82 | get 83 | { 84 | this.teaFile.SetFilePointerToItem(index); 85 | return this.teaFile.Read(); 86 | } 87 | } 88 | 89 | #endregion 90 | 91 | #region IEnumerable 92 | 93 | /// Gets the enumerator. 94 | /// The enumerator. 95 | public IEnumerator GetEnumerator() 96 | { 97 | this.teaFile.SetFilePointerToItem(0); 98 | T value; 99 | while (this.TryRead(out value)) 100 | { 101 | yield return value; 102 | } 103 | } 104 | 105 | IEnumerator IEnumerable.GetEnumerator() 106 | { 107 | return this.GetEnumerator(); 108 | } 109 | 110 | /// 111 | /// Iterator implemented using the yield statement do not allow a yield return 112 | /// statement inside a try catch block, so we encapsulate the necessary try catch 113 | /// in this method. 114 | /// 115 | /// The value. 116 | /// 117 | bool TryRead(out T value) 118 | { 119 | return this.teaFile.TryRead(out value); 120 | } 121 | 122 | #endregion 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /TeaFiles/Access/Typed/SafeBufferT.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | using System; 3 | using System.IO; 4 | using System.Runtime.InteropServices; 5 | 6 | namespace TeaTime 7 | { 8 | unsafe class SafeBuffer : SafeBuffer where T : struct 9 | { 10 | public SafeBuffer(Stream stream) : base(true) 11 | { 12 | this.stream = stream; 13 | 14 | this.Initialize(1); 15 | this.byteLength = (int)base.ByteLength; 16 | this.byteBuffer = new byte[this.byteLength]; 17 | } 18 | 19 | public T Read() 20 | { 21 | int read = this.stream.Read(this.byteBuffer, 0, this.byteLength); 22 | if (read < this.byteLength) 23 | { 24 | throw new EndOfStreamException(); 25 | } 26 | 27 | fixed (byte* p = this.byteBuffer) 28 | { 29 | base.SetHandle((IntPtr)p); 30 | return base.Read(0); 31 | } 32 | } 33 | 34 | public bool TryRead(out T value) 35 | { 36 | int read = this.stream.Read(this.byteBuffer, 0, this.byteLength); 37 | if (read < this.byteLength) 38 | { 39 | value = default(T); 40 | return false; 41 | } 42 | fixed (byte* p = this.byteBuffer) 43 | { 44 | base.SetHandle((IntPtr)p); 45 | value = base.Read(0); 46 | return true; 47 | } 48 | } 49 | 50 | public void Write(T value) 51 | { 52 | fixed (byte* p = this.byteBuffer) 53 | { 54 | base.SetHandle((IntPtr)p); 55 | base.Write(0, value); 56 | } 57 | this.stream.Write(this.byteBuffer, 0, this.byteLength); 58 | } 59 | 60 | protected override bool ReleaseHandle() 61 | { 62 | return true; 63 | } 64 | 65 | Stream stream; 66 | byte[] byteBuffer; 67 | int byteLength; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /TeaFiles/Access/UnTyped/IItemReader.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | namespace TeaTime 3 | { 4 | interface IItemReader 5 | { 6 | long Count { get; } 7 | bool CanRead { get; } 8 | Item Read(); 9 | void SetFilePointerToItem(int i); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /TeaFiles/Access/UnTyped/Item.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | using System.Linq; 3 | 4 | namespace TeaTime 5 | { 6 | /// 7 | /// Holds the values of an Item when reading a TeaFile untyped. 8 | /// 9 | /// 10 | /// If the type inside a TeaFile is not knnown or not available in the program, the file can be read untyped using . 11 | /// In contrast to which exposes each item by it's known type like Tick, exposes the 12 | /// items as a collection of instances of this class. It wraps an array of objects, each holding the value of a field of the 13 | /// item. This item can be printed by calling its method, or using 14 | /// from . 15 | /// 16 | /// 17 | /// struct Tick 18 | /// { 19 | /// public Time Time; 20 | /// public double Price; 21 | /// public long Volume; 22 | /// } 23 | /// 24 | /// ... 25 | /// 26 | /// // write typed 27 | /// using (var tf = TeaFile<Tick>.Create("acme.tea")) 28 | /// { 29 | /// tf.Write(new Tick { Time = new Time(2000, 3, 4), Price = 12.34, Volume = 7200 }); 30 | /// } 31 | /// 32 | /// // 1. read typed 33 | /// using (var tf = TeaFile<Tick>.OpenRead("acme.tea")) 34 | /// { 35 | /// Tick item = tf.Read(); // typed read is convenient: we get a tpyed Tick back, 36 | /// Time t = item.Time; // so access to its fields simply means acessing the fields of a Tick struct. 37 | /// double p = item.Price; 38 | /// long v = item.Volume; 39 | /// } 40 | /// 41 | /// // 2. read untyped 42 | /// // if we do not have the type available or do not know what is inside the file, 43 | /// // we can still read it untyped: 44 | /// using (var tf = TeaFile.OpenRead("acme.tea")) 45 | /// { 46 | /// Item item = tf.Read(); // Here an item of type Item is returned, which holds the field values as an object[]. 47 | /// object t = item.Values[0]; // The field values can be accessed by their field index 48 | /// object p = item.Values[1]; // The values inside this array still have the types Time, double and long, but 49 | /// object v = item.Values[2]; // we might not always know this at compile time. If we know it, 50 | /// // a casts can be added: 51 | /// Time tt = (Time) item.Values[0]; 52 | /// Console.WriteLine(item); // The implicit call of ToString() here will cause the output: 53 | /// // 4.3.2000, 12.34, 7200 54 | /// } 55 | /// 56 | /// 57 | /// 58 | /// Some GUI controls (like grids) accept an object[] as the value for a row, which allows very simple binding of items. 59 | /// 60 | public class Item 61 | { 62 | /// 63 | /// Constructor. 64 | /// 65 | /// 66 | /// Items are constructed by the TeaFile class and exposed via its collection. This constructor is therefore not public. 67 | /// 68 | /// The number of fields to be stored. 69 | internal Item(int fieldCount) 70 | { 71 | this.Values = new object[fieldCount]; 72 | } 73 | 74 | /// 75 | /// Gets the field values. 76 | /// 77 | /// The array holding the item's field values. 78 | public object[] Values { get; private set; } 79 | 80 | /// 81 | /// Returns a composed by the item's values. 82 | /// 83 | /// 84 | /// This method allows easy printing of an item. The returned string is the concatenation of all values inside the item, separated by a blank.
85 | /// Times will be represented by their underlying value, for instance as an Int64 value holding the number of ticks. 86 | ///
87 | /// A that represents the item. 88 | public override string ToString() 89 | { 90 | return string.Join(" ", this.Values.Select(v => v.ToString()).ToArray()); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /TeaFiles/Access/UnTyped/ItemCollection.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | 5 | namespace TeaTime 6 | { 7 | /// Provides access to items of untyped . 8 | public interface IItemCollection : IEnumerable 9 | { 10 | /// Gets the number of items in the collectiion. 11 | long Count { get; } 12 | 13 | /// Indexer to get items within this collection using array index syntax. 14 | /// The indexed item. 15 | Item this[int index] { get; } 16 | 17 | /// Returns an enumerator that iterates through the items. 18 | /// the index of the first item the enumerator shall yield. 19 | /// The enumerator. 20 | IEnumerator GetEnumerator(int startIndex); 21 | } 22 | 23 | sealed class ItemCollection : IItemCollection 24 | { 25 | #region State 26 | 27 | readonly IItemReader reader; 28 | 29 | #endregion 30 | 31 | #region Construction & Initialization 32 | 33 | /// 34 | /// This class can be instantiated by a TeaFile only, thus it is internal. 35 | /// 36 | internal ItemCollection(IItemReader reader) 37 | { 38 | this.reader = reader; 39 | } 40 | 41 | #endregion 42 | 43 | #region Public Interface 44 | 45 | /// Gets the number of items in the collectiion. 46 | public long Count 47 | { 48 | get { return this.reader.Count; } 49 | } 50 | 51 | /// Indexer to get items within this collection using array index syntax. 52 | /// The indexed item. 53 | public Item this[int index] 54 | { 55 | get 56 | { 57 | this.reader.SetFilePointerToItem(index); 58 | var item = this.reader.Read(); 59 | return item; 60 | } 61 | } 62 | 63 | /// Gets the enumerator over all item. 64 | /// The enumerator. 65 | public IEnumerator GetEnumerator() 66 | { 67 | return this.GetEnumerator(0); 68 | } 69 | 70 | /// Gets an enumerator of a range starting at . 71 | /// The start index. 72 | /// The enumerator. 73 | public IEnumerator GetEnumerator(int startIndex) 74 | { 75 | this.reader.SetFilePointerToItem(startIndex); 76 | while (this.reader.CanRead) 77 | { 78 | yield return this.reader.Read(); 79 | } 80 | } 81 | 82 | #endregion 83 | 84 | #region IEnumerable Members 85 | 86 | IEnumerator IEnumerable.GetEnumerator() 87 | { 88 | return this.GetEnumerator(0); 89 | } 90 | 91 | IEnumerator IEnumerable.GetEnumerator() 92 | { 93 | return ((IEnumerable)this).GetEnumerator(); 94 | } 95 | 96 | #endregion 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /TeaFiles/Base/Event.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | namespace TeaTime 3 | { 4 | /// 5 | /// Represents an event. 6 | /// 7 | /// 8 | /// 9 | /// An event describes a value at a certain time. This value might be an observation 10 | /// like the temperature in Costa Rica, the price of a transaction or an assumed value like number of people waiting in a queue computed by a simulation. 11 | /// Such value might be represented by a scalar value, like a for a temperature or require a 12 | /// structured value like a "Tick" holding price and volume. While the type of such value differs, ab evene always associates it with 13 | /// a time value. This class abstracts this time property. 14 | /// 15 | /// 16 | /// Instead of using

17 | /// 18 | /// struct Tick 19 | /// { 20 | /// [EventTime] 21 | /// public Time Time; 22 | /// public double Price; 23 | /// public int Volume; 24 | /// } 25 | /// 26 | /// Event<T> allows to abbreviate 27 | /// 28 | /// struct Tick 29 | /// { 30 | /// public double Price; 31 | /// public int Volume; 32 | /// } 33 | /// // and then use 34 | /// Event<Tick> 35 | /// 36 | /// This has several advantages: 37 | /// 38 | /// Processing frameworks can isolate the value from or construct timestamped instances from values. 39 | /// The fact that a type is an event is made explicit in code 40 | /// It is shorted to write, is packed into , 41 | /// The type used for time repressentation is encapsulated in a single class. 42 | /// 43 | ///
44 | ///
45 | /// The type of the event value. 46 | public struct Event where T : struct 47 | { 48 | /// 49 | /// Constructor, this overload is often handy to create instances. 50 | /// 51 | /// The event time. 52 | /// The event value. 53 | public Event(Time time, T value) 54 | { 55 | this.Time = time; 56 | this.Value = value; 57 | } 58 | 59 | /// 60 | /// The time of the event. 61 | /// 62 | /// 63 | /// The type supports flexible epoch and resolution values.
64 | ///
65 | [EventTime] 66 | public Time Time; 67 | 68 | /// 69 | /// The event value. 70 | /// 71 | /// Dependent on the application domain, this might be a scalar value or a struct. 72 | public T Value; 73 | 74 | /// Returns the fully qualified type name of this instance. 75 | /// A containing a fully qualified type name. 76 | public override string ToString() 77 | { 78 | return "{0}\t{1}".Formatted(this.Time, this.Value); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /TeaFiles/Description/DescriptionSource.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | namespace TeaTime 3 | { 4 | /// 5 | /// Describes the origin of an ItemDescription. 6 | /// 7 | /// 8 | /// ItemDescriptions are either ccreated by reflecting and analyzing a .Net Struct or are read from a file. 9 | /// 10 | enum DescriptionSource 11 | { 12 | /// 13 | /// The value has not been set. 14 | /// 15 | None = 0, 16 | 17 | /// 18 | /// The ItemDescription was read from a TeaFile. 19 | /// 20 | File = 1, 21 | 22 | /// 23 | /// The ItemDescription was created by reflecting and analyzing the item's type. 24 | /// 25 | ItemType = 2 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /TeaFiles/Description/FieldType.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | using System; 3 | 4 | namespace TeaTime 5 | { 6 | /// 7 | /// Describes the types available for structs inside TeaFiles. 8 | /// 9 | /// 10 | /// In order to be platform compatible, only the first 10 types should be used (signed and unsigned integers, float and double). 11 | /// Some applications have a very limited type set, so even tighter restriction is sometimes favorable. 12 | /// To exchange data with R for instance, usage should be restricted to signed 32 bit integers and double. 13 | /// 14 | /// 15 | public enum FieldType 16 | { 17 | /// Invalid value. 18 | None = 0, 19 | 20 | // platform agnostic 21 | /// signed 1 byte value. 22 | Int8 = 1, 23 | /// signed 2 byte value. 24 | Int16 = 2, 25 | /// signed 4 byte value. 26 | Int32 = 3, 27 | /// signed 8 byte value. 28 | Int64 = 4, 29 | 30 | /// unsigned 1 byte value. 31 | UInt8 = 5, 32 | /// unsigned 2 byte value. 33 | UInt16 = 6, 34 | /// unsigned 4 byte value. 35 | UInt32 = 7, 36 | /// unsigned 8 byte value. 37 | UInt64 = 8, 38 | 39 | /// 4 byte IEEE 754 floating point value. 40 | Float = 9, 41 | /// 8 byte IEEE 754 floating point value. 42 | Double = 10, 43 | 44 | // platform specific 45 | /// .Net specific type. 46 | NetDecimal = 0x200, 47 | 48 | // private extensions must have integer identifiers above 0x1000. 49 | /// Custom Field Types should have identifiers above 0x1000. 50 | Custom = 0x1000 51 | } 52 | 53 | static partial class Extensions 54 | { 55 | public static FieldType GetFieldType(this Type t) 56 | { 57 | if (t == typeof (sbyte)) return FieldType.Int8; 58 | if (t == typeof (Int16)) return FieldType.Int16; 59 | if (t == typeof (Int32)) return FieldType.Int32; 60 | if (t == typeof (Int64)) return FieldType.Int64; 61 | 62 | if (t == typeof (byte)) return FieldType.UInt8; 63 | if (t == typeof (UInt16)) return FieldType.UInt16; 64 | if (t == typeof (UInt32)) return FieldType.UInt32; 65 | if (t == typeof (UInt64)) return FieldType.UInt64; 66 | 67 | if (t == typeof (float)) return FieldType.Float; 68 | if (t == typeof (double)) return FieldType.Double; 69 | 70 | if (t == typeof (DateTime)) 71 | { 72 | throw new InvalidFieldTypeException("DateTime is not supported as a field type. Use TeaTime.Time instead."); 73 | } 74 | if (t == typeof (Time)) return FieldType.Int64; // in doubt we use signed integers to avoid computational issues with signed values 75 | 76 | if (t == typeof (decimal)) return FieldType.NetDecimal; 77 | 78 | throw new ArgumentOutOfRangeException( 79 | "Incompatible field: The type {0} cannot be used inside the item of a TeaFile. " + 80 | "Change the type of the field or exclude it.".Formatted(t.FullName)); 81 | } 82 | 83 | public static int GetSize(this FieldType fieldType) 84 | { 85 | switch (fieldType) 86 | { 87 | case FieldType.Int8: 88 | return 1; 89 | case FieldType.Int16: 90 | return 2; 91 | case FieldType.Int32: 92 | return 4; 93 | case FieldType.Int64: 94 | return 8; 95 | 96 | case FieldType.UInt8: 97 | return 1; 98 | case FieldType.UInt16: 99 | return 2; 100 | case FieldType.UInt32: 101 | return 4; 102 | case FieldType.UInt64: 103 | return 8; 104 | 105 | case FieldType.Float: 106 | return 4; 107 | case FieldType.Double: 108 | return 8; 109 | 110 | case FieldType.NetDecimal: 111 | return 16; 112 | 113 | case FieldType.None: 114 | throw new InvalidOperationException("FieldType was not set to a valid type."); 115 | 116 | default: 117 | throw new ArgumentOutOfRangeException("The FieldType '{0}' is not supported by this API.".Formatted(fieldType)); 118 | } 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /TeaFiles/Description/ItemDescriptionElements.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | using System; 3 | 4 | namespace TeaTime 5 | { 6 | /// Describes the elements inside an . 7 | /// 8 | /// 9 | /// A TeaFile holds a description of the item type it stores. This item description has several elements: 10 | ///
    11 | ///
  • the name of the item type (e.g. "Tick", "OHLCV")
  • 12 | ///
  • the size of the item
  • 13 | ///
  • the number of fields and their
  • 14 | ///
  • name,
  • 15 | ///
  • offset and
  • 16 | ///
  • type.
  • 17 | ///
18 | /// A value of this type specifies which elements shall be compared when an existing file is opened with a specific item type. 19 | ///
20 | /// 21 | /// When a typed is opened, the of {T} is compared against those in the file. If it does not match those 22 | /// stored in the file, an exception is thrown. This ensures that a TeaFile is read with the correct type. For instance, 23 | /// a file that was written using the item type 24 | /// 25 | /// struct Tick 26 | /// { 27 | /// public Time Time; 28 | /// public double Price; 29 | /// } 30 | /// 31 | /// is not accessible using 32 | /// 33 | /// struct A 34 | /// { 35 | /// public Time Time; 36 | /// public short Value1; 37 | /// public float Value2; 38 | /// public double Value3; 39 | /// } 40 | /// TeaFile<A>.OpenRead("lab.tea"); 41 | /// 42 | /// because A has a different type name ("A" vs "Tick"), a different size and field names, types and offsets. 43 | /// Sometimes however, such strong check is undesired. If just the name of the type or the name of a field is altered, 44 | /// the file should still be accessible with a partially different type than the one used for writing. The optional 45 | /// argument elementsToValidate in the OpenRead, Write and Append methods of allows this: 46 | /// 47 | /// 48 | /// struct Tick 49 | /// { 50 | /// public Time Time; 51 | /// public double Price; 52 | /// } 53 | /// TeaFile<Tick>.Create("lab.tea"); 54 | /// 55 | /// // now we read this file using another type. 56 | /// struct NewTick 57 | /// { 58 | /// public Time NewTime; 59 | /// public double NewPrice; 60 | /// } 61 | /// TeaFile<NewTick>.OpenRead("lab.tea", ItemDescriptionElements.FieldTypes); 62 | /// 63 | /// 64 | /// Since we open the file with a relaxed check that compares field types (and offsets), the call will succeed and 65 | /// the file will be readable with the new type. 66 | /// 67 | ///
68 | /// 69 | /// 70 | /// 71 | [Flags] 72 | public enum ItemDescriptionElements 73 | { 74 | /// no item description part. 75 | None = 0, 76 | /// the item . 77 | ItemName = 1, 78 | /// the item size. 79 | ItemSize = 2, 80 | /// field offsets. 81 | FieldOffsets = 4, 82 | /// field names. 83 | FieldNames = 4 + 8, 84 | /// field types. 85 | FieldTypes = 4 + 16, 86 | 87 | /// all item description parts. 88 | All = 0xff 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /TeaFiles/Description/TeaFileDescription.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | using System.Collections.ObjectModel; 3 | using System.Text; 4 | 5 | namespace TeaTime 6 | { 7 | /// 8 | /// TeaFiles optionally hold a description, describing item layout and content. 9 | /// 10 | /// 11 | /// The description of a TeaFile describes 12 | ///
    13 | ///
  • the layout of the items (),
  • 14 | ///
  • its content as a string ()
  • 15 | ///
  • its content as a collection of () pairs
  • 16 | ///
  • the for representation of time ().
  • 17 | ///
18 | ///
19 | public class TeaFileDescription 20 | { 21 | /// 22 | /// Returns the , descibing the layout of the items stored in the file. 23 | /// If the file holds no description of its layout, null will be returned. 24 | /// 25 | public ItemDescription ItemDescription { get; internal set; } 26 | 27 | /// Gets or sets the time scale. 28 | /// The time scale. 29 | /// 30 | public Timescale? Timescale { get; internal set; } 31 | 32 | /// Gets or sets a string describing the content. 33 | /// "Silver Prices", "NYC Temperature" 34 | public string ContentDescription { get; internal set; } 35 | 36 | /// Gets or sets a list of pairs. 37 | /// 38 | /// "decimals" = 2 39 | /// "data source" = "Reuters" 40 | /// "id" = {8087E80F-F031-48A1-B1AC-102E51BD173A} 41 | /// 42 | public NameValueCollection NameValues { get; internal set; } 43 | 44 | /// Gets or sets the time field offsets. 45 | /// Most times this list will hold a single value which in turn will most times be 0. For instance, the 46 | /// following TeaFile will have such a list of {0}: 47 | /// 48 | /// struct Tick 49 | /// { 50 | /// public Time Time; 51 | /// public double Price; 52 | /// public long Volume; 53 | /// } 54 | /// TeaFile<Tick>.Create("acme"); 55 | /// 56 | /// Since the Time field above will be at offset=0 inside the item and only a single time fields exists in the type, 57 | /// the TimeFieldOffsets = {0}. 58 | public ReadOnlyCollection TimeFieldOffsets { get; internal set; } 59 | 60 | /// Returns a that represents the current . 61 | /// A that represents the current . 62 | public override string ToString() 63 | { 64 | var sb = new StringBuilder(); 65 | sb.AppendLine("... TeaFile Description ..."); 66 | sb.AppendLine("#Item"); 67 | sb.AppendLine(this.ItemDescription.SafeToString()); 68 | sb.AppendLine("#Content"); 69 | sb.AppendLine(this.ContentDescription.SafeToString()); 70 | sb.AppendLine("#NameValues"); 71 | sb.AppendLine(this.NameValues.SafeToString()); 72 | sb.AppendLine("... TeaFile Description End ..."); 73 | return sb.ToString(); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /TeaFiles/Description/TimeFormat.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace TeaTime 7 | { 8 | /// 9 | /// Describes the time format applicable to the ticks stored in the time series items.

10 | /// This API allows Time Series with timestamps that have different resolutions and epoch starts. 11 | /// Commonly used time representations count the ticks from some point in time. Usually the ticks 12 | /// count the number of 13 | /// 14 | /// Seconds (e.g. Ruby's Time class before 1.9) 15 | /// Milliseconds (e.g. java.util.Date) 16 | /// 0.1 Microseconds = 100 Nanoseconds (e.g. .Net System.DateTime) 17 | /// 18 | /// can map handle all these different formats by setting it's static property 19 | /// to an instance of that holds the epoch offset and 20 | /// tick resolution to be used. 21 | /// 22 | /// This class describes a scale while class is used for computation. 23 | ///
24 | public class TimeFormat 25 | { 26 | /// 27 | /// This is the number of days since 1.1.1 at which the epoch starts. 28 | /// 29 | public long Epoch { get; set; } 30 | 31 | /// 32 | /// The number of ticks per day. If time resolution is required only at the day level this can be set to 1. 33 | /// 34 | public long TicksPerDay { get; set; } 35 | 36 | public Timescale Scale 37 | { 38 | get { return Timescale.FromEpoch(this.Epoch, this.TicksPerDay); } 39 | } 40 | 41 | public bool Equals(Timescale scale) 42 | { 43 | return this.Epoch == scale.Epoch && 44 | this.TicksPerDay == scale.TicksPerDay; 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /TeaFiles/Header/ISectionFormatter.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | namespace TeaTime.Header 3 | { 4 | /// 5 | /// Each section in a TeaFile's header is identifed by an Id and can be written and read.

6 | /// Passing context instances to the Read and Write methods exposes the description object to be read 7 | /// or populated. 8 | ///
9 | interface ISectionFormatter 10 | { 11 | /// 12 | /// The section ID.

13 | /// Section IDs are defined in the TeaFile format definition. Custom sections can be added whose IDs 14 | /// must be in the range as specified in the TeaFile definition. 15 | ///
16 | int Id { get; } 17 | 18 | /// 19 | /// Writes the section into the file taking the description values from the context's Description property. 20 | /// 21 | /// 22 | void Write(WriteContext c); 23 | 24 | /// 25 | /// Reads the section and adds the values extracted from the file to the Description in the ReadContext argument. 26 | /// 27 | /// 28 | void Read(ReadContext c); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /TeaFiles/Header/ReadContext.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | using TeaTime.IO; 3 | 4 | namespace TeaTime.Header 5 | { 6 | /// 7 | /// During header reading, the ReaContext provides section descriptions 8 | /// 9 | class ReadContext 10 | { 11 | public ReadContext(IFormattedReader reader) 12 | { 13 | this.Description = new TeaFileDescription(); 14 | this.Reader = reader; 15 | } 16 | 17 | public IFormattedReader Reader { get; private set; } 18 | public TeaFileDescription Description { get; private set; } 19 | 20 | public long ItemAreaStart { get; set; } 21 | public long ItemAreaEnd { get; set; } 22 | public long SectionCount { get; set; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /TeaFiles/Header/Sections/ContentSectionFormatter.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | using System; 3 | 4 | namespace TeaTime.Header 5 | { 6 | /// 7 | /// The ContentDescription holds a plain string desribing what the file stores.

8 | /// Example: "EUR/USD" or "Temperature New York City" 9 | ///
10 | class ContentSectionFormatter : ISectionFormatter 11 | { 12 | public Int32 Id 13 | { 14 | get { return 0x80; } 15 | } 16 | 17 | public void Write(WriteContext c) 18 | { 19 | if (c == null) throw new ArgumentNullException("c"); 20 | if (c.Description == null) return; 21 | if (!c.Description.ContentDescription.IsSet()) return; 22 | c.Writer.WriteText(c.Description.ContentDescription); 23 | } 24 | 25 | public void Read(ReadContext c) 26 | { 27 | if (c == null) throw new ArgumentNullException("c"); 28 | c.Description.ContentDescription = c.Reader.ReadText(); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /TeaFiles/Header/Sections/ItemSectionFormatter.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | namespace TeaTime.Header 3 | { 4 | /// 5 | /// A TeaFile's ItemSection stores the layout of the Item's fields. 6 | /// 7 | class ItemSectionFormatter : ISectionFormatter 8 | { 9 | public int Id 10 | { 11 | get { return 0x0a; } 12 | } 13 | 14 | public void Write(WriteContext c) 15 | { 16 | if (c.Description == null) return; 17 | var id = c.Description.ItemDescription; 18 | if (id == null) return; 19 | var w = c.Writer; 20 | w.WriteInt32(id.ItemSize); 21 | w.WriteText(id.ItemTypeName); 22 | w.WriteInt32(id.Fields.Count); 23 | foreach (Field f in id.Fields) 24 | { 25 | w.WriteInt32((int)f.FieldType); 26 | w.WriteInt32(f.Offset); 27 | w.WriteText(f.Name); 28 | } 29 | } 30 | 31 | public void Read(ReadContext c) 32 | { 33 | var id = new ItemDescription(DescriptionSource.File); 34 | var r = c.Reader; 35 | id.ItemSize = r.ReadInt32(); 36 | id.ItemTypeName = r.ReadText(); 37 | var fieldCount = r.ReadInt32(); 38 | fieldCount.Times(() => 39 | { 40 | var f = id.NewField(); 41 | f.FieldType = (FieldType)r.ReadInt32(); 42 | f.Offset = r.ReadInt32(); 43 | f.Name = r.ReadText(); 44 | }); 45 | c.Description.ItemDescription = id; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /TeaFiles/Header/Sections/NameValueSectionFormatter.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | using System; 3 | using System.Linq; 4 | 5 | namespace TeaTime.Header 6 | { 7 | /// 8 | /// The NameValue section holds a collection of Name / Value pairs. See or details of the value types. 9 | /// 10 | class NameValueSectionFormatter : ISectionFormatter 11 | { 12 | public Int32 Id 13 | { 14 | get { return 0x81; } 15 | } 16 | 17 | public void Write(WriteContext c) 18 | { 19 | if (c == null) throw new ArgumentNullException("c"); 20 | if (c.Description == null) return; 21 | if (c.Description.NameValues == null) return; 22 | if (c.Description.NameValues.Count == 0) return; 23 | 24 | var nvs = c.Description.NameValues; 25 | c.Writer.WriteInt32(nvs.Count()); 26 | nvs.ForEach(nv => c.Writer.WriteNameValue(nv)); 27 | } 28 | 29 | public void Read(ReadContext c) 30 | { 31 | if (c == null) throw new ArgumentNullException("c"); 32 | int count = c.Reader.ReadInt32(); 33 | c.Description.NameValues = new NameValueCollection(); 34 | count.Times(() => c.Description.NameValues.Add(c.Reader.ReadNameValue())); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /TeaFiles/Header/Sections/TimeSectionFormatter.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace TeaTime.Header 6 | { 7 | /// 8 | /// This section persists the time epoch and resolution used for time values and the 9 | /// offsets of fields that hold time times. 10 | /// 11 | /// 12 | /// If more than one field of the time series 13 | /// item holds time values, the first field in the collection is the event time of the 14 | /// time series that must have monotonically increasing values. 15 | /// 16 | class TimeSectionFormatter : ISectionFormatter 17 | { 18 | public int Id 19 | { 20 | get { return 0x40; } 21 | } 22 | 23 | public void Write(WriteContext c) 24 | { 25 | if (c.Description == null) return; 26 | if (c.Description.ItemDescription == null) return; 27 | var timeFields = c.Description.ItemDescription.Fields.Where(f => f.IsTime).ToList(); 28 | if (!timeFields.Any()) return; 29 | 30 | // time format 31 | if (!c.Description.Timescale.HasValue) 32 | { 33 | // TeaFile.Create will assign the default Timescale to the descrption, 34 | // so Timescale should always be set. 35 | throw new InternalErrorException(); 36 | } 37 | c.Writer.WriteInt64(c.Description.Timescale.Value.Epoch); 38 | c.Writer.WriteInt64(c.Description.Timescale.Value.TicksPerDay); 39 | 40 | // time fields 41 | c.Writer.WriteInt32(timeFields.Count); 42 | timeFields.ForEach(f => c.Writer.WriteInt32(f.Offset)); 43 | } 44 | 45 | public void Read(ReadContext c) 46 | { 47 | // time scale 48 | var epoch = c.Reader.ReadInt64(); 49 | var ticksPerDay = c.Reader.ReadInt64(); 50 | c.Description.Timescale = Timescale.FromEpoch(epoch, ticksPerDay); 51 | 52 | // time fields 53 | var timeFieldsCount = c.Reader.ReadInt32(); 54 | var offsets = new List(); 55 | timeFieldsCount.Times(() => 56 | offsets.Add(c.Reader.ReadInt32())); 57 | c.Description.TimeFieldOffsets = offsets.AsReadOnly(); 58 | 59 | // adorn item description with time aspects, if available 60 | var id = c.Description.ItemDescription; 61 | if (id != null) 62 | { 63 | bool isFirstTimeField = true; 64 | foreach (var offset in offsets) 65 | { 66 | var f = id.FindFieldByOffset(offset); 67 | if (f == null) throw new FileFormatException("Time format section contains an entry for a field at offset {0} but no such field was found in the item description."); 68 | f.IsTime = true; 69 | f.IsEventTime = isFirstTimeField; 70 | isFirstTimeField = false; 71 | } 72 | } 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /TeaFiles/Header/WriteContext.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | using TeaTime.IO; 3 | 4 | namespace TeaTime.Header 5 | { 6 | /// 7 | /// An instance of this class is passed to all section formatters's method, providing the 8 | /// formatter access to the and the writer. 9 | /// 10 | class WriteContext 11 | { 12 | public WriteContext(IFormattedWriter writer) 13 | { 14 | this.Writer = writer; 15 | } 16 | 17 | public IFormattedWriter Writer { get; set; } 18 | public TeaFileDescription Description { get; set; } 19 | public long ItemAreaStart { get; set; } 20 | public int SectionCount { get; set; } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /TeaFiles/IO/FileIO.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | using System; 3 | using System.IO; 4 | 5 | namespace TeaTime.IO 6 | { 7 | /// 8 | /// Exposes low level file IO methods. This class collects the whole functionality required from the file system api of an operating system or 9 | /// language API. Notably, modifications to the file position are not required. 10 | /// 11 | /// This class does not own the stream passed in the ctor, so it does not implement IDisposable.

12 | class FileIO : IFileIO 13 | { 14 | Stream stream; 15 | BinaryReader reader; 16 | BinaryWriter writer; 17 | 18 | public FileIO(Stream stream) 19 | { 20 | this.stream = stream; 21 | } 22 | 23 | BinaryReader Reader 24 | { 25 | get { return this.reader ?? (this.reader = new BinaryReader(this.stream)); } 26 | } 27 | 28 | BinaryWriter BinaryWriter 29 | { 30 | get { return this.writer ?? (this.writer = new BinaryWriter(this.stream)); } 31 | } 32 | 33 | public long Position 34 | { 35 | get { return this.stream.Position; } 36 | } 37 | 38 | /// 39 | /// Reads a 32bit integer value from the file. 40 | /// 41 | /// The int32 value. 42 | /// 43 | public Int32 ReadInt32() 44 | { 45 | return this.Reader.ReadInt32(); 46 | } 47 | 48 | /// 49 | /// Reads a 64bit integer value from the file. 50 | /// 51 | /// The int64 value. 52 | public Int64 ReadInt64() 53 | { 54 | return this.Reader.ReadInt64(); 55 | } 56 | 57 | /// 58 | /// Reads a double value from the file. 59 | /// 60 | /// The double value. 61 | public double ReadDouble() 62 | { 63 | return this.Reader.ReadDouble(); 64 | } 65 | 66 | public byte[] ReadBytes(int count) 67 | { 68 | return this.Reader.ReadBytes(count); 69 | } 70 | 71 | /// 72 | /// This implementation performance n times ReadByte().

73 | /// Alternatively we could set the file position directly but we want to avoid usage of the 74 | /// filepointer functions to demonstrate that they are not necessary. This might be important 75 | /// when TeaFile APIs are written in languages that do not have a rich API is .Net. For 76 | /// instance, R might not have any filepointer modification functions in its own language. 77 | ///
78 | /// The number of bytes to skip. 79 | public void SkipBytes(int n) 80 | { 81 | n.Times(() => this.Reader.ReadByte()); 82 | } 83 | 84 | public void WriteInt32(Int32 value) 85 | { 86 | this.BinaryWriter.Write(value); 87 | } 88 | 89 | public void WriteInt64(Int64 value) 90 | { 91 | this.BinaryWriter.Write(value); 92 | } 93 | 94 | public void WriteDouble(double value) 95 | { 96 | this.BinaryWriter.Write(value); 97 | } 98 | 99 | public void WriteBytes(byte[] bytes) 100 | { 101 | this.BinaryWriter.Write(bytes); 102 | } 103 | 104 | public void WriteZeroByte() 105 | { 106 | this.stream.WriteByte(0); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /TeaFiles/IO/FormattedReader.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | using System; 3 | using System.Text; 4 | 5 | namespace TeaTime.IO 6 | { 7 | /// 8 | /// FormattedReader provides higher level file reading on top of . In the sense of 9 | /// a strict layer pattern, were direct calls to the underlying layer are not allowed, FormattedReader 10 | /// also exposes reading of int32, int64 and double and Guid values. 11 | /// 12 | /// Methods are explained in 13 | class FormattedReader : IFormattedReader 14 | { 15 | readonly IFileIO fileIO; 16 | 17 | public FormattedReader(IFileIO fileIO) 18 | { 19 | this.fileIO = fileIO; 20 | } 21 | 22 | public int ReadInt32() 23 | { 24 | return this.fileIO.ReadInt32(); 25 | } 26 | 27 | public long ReadInt64() 28 | { 29 | return this.fileIO.ReadInt64(); 30 | } 31 | 32 | public double ReadDouble() 33 | { 34 | return this.fileIO.ReadDouble(); 35 | } 36 | 37 | Guid ReadGuid() 38 | { 39 | var bytes = this.fileIO.ReadBytes(16); 40 | return new Guid(bytes); 41 | } 42 | 43 | public string ReadText() 44 | { 45 | var bytes = this.ReadLengthPrefixedBytes(); 46 | var encoding = new UTF8Encoding(false, true); 47 | return encoding.GetString(bytes); 48 | } 49 | 50 | public NameValue ReadNameValue() 51 | { 52 | string name = this.ReadText(); 53 | NameValue.ValueKind kind = (NameValue.ValueKind)this.ReadInt32(); 54 | switch (kind) 55 | { 56 | case NameValue.ValueKind.Text: 57 | string s = this.ReadText(); 58 | return new NameValue(name, s); 59 | case NameValue.ValueKind.Int32: 60 | int n = this.ReadInt32(); 61 | return new NameValue(name, n); 62 | case NameValue.ValueKind.Double: 63 | double d = this.ReadDouble(); 64 | return new NameValue(name, d); 65 | case NameValue.ValueKind.Guid: 66 | Guid g = this.ReadGuid(); 67 | return new NameValue(name, g); 68 | default: 69 | throw new ArgumentOutOfRangeException("NameValueKind iss not supported".Formatted(kind)); 70 | } 71 | } 72 | 73 | public void SkipBytes(int n) 74 | { 75 | this.fileIO.SkipBytes(n); 76 | } 77 | 78 | public long Position 79 | { 80 | get { return this.fileIO.Position; } 81 | } 82 | 83 | byte[] ReadLengthPrefixedBytes() 84 | { 85 | var count = this.fileIO.ReadInt32(); 86 | if (count == 0) return new byte[0]; 87 | return this.fileIO.ReadBytes(count); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /TeaFiles/IO/FormattedWriter.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | using System; 3 | using System.Text; 4 | 5 | namespace TeaTime.IO 6 | { 7 | /// 8 | /// FormattedWriter provides higher level file writing on top of . In the sense of 9 | /// a strict layer pattern, were direct calls to the underlying layer are not allowed, FormattedWriter 10 | /// also exposes writing of int32, int64 and double and Guid values. 11 | /// 12 | /// Methods are explained in 13 | class FormattedWriter : IFormattedWriter 14 | { 15 | IFileIO fileIO; 16 | 17 | public FormattedWriter(IFileIO fileIO) 18 | { 19 | this.fileIO = fileIO; 20 | } 21 | 22 | public void WriteInt32(int value) 23 | { 24 | this.fileIO.WriteInt32(value); 25 | } 26 | 27 | public void WriteInt64(long value) 28 | { 29 | this.fileIO.WriteInt64(value); 30 | } 31 | 32 | public void WriteDouble(double value) 33 | { 34 | this.fileIO.WriteDouble(value); 35 | } 36 | 37 | /// 38 | /// Writes a 16 byte Guid into the file. 39 | /// 40 | /// The value to write. 41 | /// 42 | public void WriteGuid(Guid value) 43 | { 44 | this.fileIO.WriteBytes(value.ToByteArray()); 45 | } 46 | 47 | public void WriteRaw(byte[] bytes) 48 | { 49 | if (bytes == null) throw new ArgumentNullException("bytes"); 50 | this.fileIO.WriteBytes(bytes); 51 | } 52 | 53 | public void WriteText(string text) 54 | { 55 | var encoding = new UTF8Encoding(false, true); 56 | byte[] bytes = encoding.GetBytes(text ?? ""); 57 | this.WriteLengthPrefixedBytes(bytes); 58 | } 59 | 60 | public void WriteNameValue(NameValue nameValue) 61 | { 62 | this.WriteText(nameValue.Name); 63 | this.WriteInt32((int)nameValue.Kind); 64 | switch (nameValue.Kind) 65 | { 66 | case NameValue.ValueKind.Text: 67 | this.WriteText(nameValue.GetValue()); 68 | break; 69 | case NameValue.ValueKind.Int32: 70 | this.WriteInt32(nameValue.GetValue()); 71 | break; 72 | case NameValue.ValueKind.Double: 73 | this.WriteDouble(nameValue.GetValue()); 74 | break; 75 | case NameValue.ValueKind.Guid: 76 | this.WriteGuid(nameValue.GetValue()); 77 | break; 78 | default: 79 | throw new ArgumentOutOfRangeException("NameValueKind is not supported".Formatted(nameValue.Kind)); 80 | } 81 | } 82 | 83 | void WriteLengthPrefixedBytes(byte[] bytes) 84 | { 85 | if (bytes == null || bytes.Length == 0) 86 | { 87 | this.fileIO.WriteInt32(0); 88 | } 89 | else 90 | { 91 | // length 92 | this.fileIO.WriteInt32(bytes.Length); 93 | // bytes 94 | this.fileIO.WriteBytes(bytes); 95 | } 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /TeaFiles/Layout/AnalyzedField.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | namespace TeaTime 3 | { 4 | /// 5 | /// A field within the layout analysis process. 6 | /// 7 | class AnalyzedField 8 | { 9 | readonly FieldPath fieldPath; 10 | readonly int offset; 11 | string name; 12 | 13 | public AnalyzedField(FieldPath fieldPath, int offset) 14 | { 15 | this.fieldPath = fieldPath; 16 | this.offset = offset; 17 | this.name = this.FieldPath.DotPath; 18 | } 19 | 20 | public string Name 21 | { 22 | get { return this.name; } 23 | set { this.name = value; } 24 | } 25 | 26 | public FieldPath FieldPath 27 | { 28 | get { return this.fieldPath; } 29 | } 30 | 31 | public int Offset 32 | { 33 | get { return this.offset; } 34 | } 35 | 36 | public override string ToString() 37 | { 38 | return string.Join(" ", this.name, this.FieldPath.Last.FieldType, this.FieldPath, this.offset); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /TeaFiles/Layout/ByteSearcher.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | namespace TeaTime 3 | { 4 | class ByteSearcher 5 | { 6 | /// 7 | /// Finds the position of inside . 8 | /// 9 | /// The search space. 10 | /// The search pattern. 11 | /// Length of the search space. 12 | /// Length of the pattern. 13 | /// THe byte position of the pattern. 14 | /// The pattern is not included in the search space. 15 | /// 16 | public static unsafe int GetPosition(byte* searchSpace, byte* searchPattern, int searchSpaceLength, int patternLength) 17 | { 18 | int n = searchSpaceLength - patternLength + 1; 19 | for (int i = 0; i < n; i++) 20 | { 21 | if (StartsWith(searchSpace++, searchPattern, patternLength)) return i; 22 | } 23 | throw new InternalErrorException("Pattern not found: The search pattern was not found inside search space."); 24 | } 25 | 26 | /// 27 | /// Tests whether starts with the byte sequence . 28 | /// 29 | /// The search space. 30 | /// The search pattern. 31 | /// Length of the pattern. 32 | /// true if the pattern matches the begin of the search space 33 | /// The length of the search space is not checked. The caller must assure that the search space is at least as long as the pattern length. 34 | internal static unsafe bool StartsWith(byte* searchSpace, byte* searchPattern, int patternLength) 35 | { 36 | while (patternLength-- > 0) 37 | { 38 | if (*searchSpace++ != *searchPattern++) return false; 39 | } 40 | return true; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /TeaFiles/Layout/FieldPath.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Reflection; 6 | 7 | namespace TeaTime 8 | { 9 | class FieldPath 10 | { 11 | readonly List fields; 12 | 13 | public FieldPath() 14 | { 15 | this.fields = new List(); 16 | } 17 | 18 | FieldPath(IEnumerable fieldInfos) 19 | { 20 | this.fields = new List(fieldInfos); 21 | } 22 | 23 | public List Fields 24 | { 25 | get { return this.fields; } 26 | } 27 | 28 | public FieldInfo Last 29 | { 30 | get { return this.fields.Last(); } 31 | } 32 | 33 | public string DotPath 34 | { 35 | get 36 | { 37 | // filter Value field from Event 38 | return string.Join(".", this.fields.Where(f => !IsTeaTimeEventDataToSkip(f)).Select(f => f.Name)); 39 | } 40 | } 41 | 42 | /// 43 | /// The value part of is skipped if its type is a struct. 44 | /// 45 | /// The f. 46 | /// true if the event shall be skipped; otherwise, false. 47 | /// This avoids creating fieldnames "Time, Value.Price, Value.Volume" for Event<Trade>.

48 | /// If however the type is a primitive, like a double, then this double shall appear as field with name "Value". 49 | ///
50 | static bool IsTeaTimeEventDataToSkip(FieldInfo f) 51 | { 52 | if (f.Name != "Value") return false; 53 | if (f.FieldType.IsPrimitive) return false; 54 | Type t = f.DeclaringType; 55 | return t.IsGenericType && (t.GetGenericTypeDefinition() == typeof (Event<>)); 56 | } 57 | 58 | public FieldPath AppendChild(FieldInfo fi) 59 | { 60 | FieldPath childPath = new FieldPath(this.fields); 61 | childPath.fields.Add(fi); 62 | return childPath; 63 | } 64 | 65 | public override string ToString() 66 | { 67 | return this.DotPath; 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /TeaFiles/License.txt: -------------------------------------------------------------------------------- 1 | This source code is released under the GNU General Public License v3. 2 | http://www.gnu.org/copyleft/gpl.html 3 | 4 | In addition to the terms of this license, use and distribution of this code shall be attributed to discretelogics, referencing "www.discretelogics.com". 5 | 6 | If you intend to ship TeaFiles as part of a commercial product, or have other uses not covered by the license, please contact office@discretelogics.com. -------------------------------------------------------------------------------- /TeaFiles/TeaFiles.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | c85b6ff7-387d-48fa-9377-e2c9dd76feb6 5 | netstandard2.0 6 | true 7 | false 8 | TeaFiles.snk 9 | true 10 | 11 | 12 | 13 | DiscreteLogics 14 | TeaFiles.Net 15 | 2.0.0 16 | 2.0.0.0 17 | DiscreteLogics and github contributors 18 | Time Series storage in flat files 19 | 2.0.0.0 20 | 21 | 22 | 23 | Daffy Duck 24 | netstandard;netcore;TeaFiles 25 | https://github.com/discretelogics/TeaFiles.Net-time-series-storage-in-flat-files 26 | NONE 27 | Unlicense 28 | https://github.com/discretelogics/TeaFiles.Net-time-series-storage-in-flat-files 29 | git 30 | 31 | 32 | 33 | 34 | <_Parameter1>DiscreteLogics.TeaFile.Tests 35 | 36 | 37 | <_Parameter1>DynamicProxyGenAssembly2 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /TeaFiles/TeaFiles.ruleset: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /TeaFiles/TeaTime.cs: -------------------------------------------------------------------------------- 1 | // copyright discretelogics 2012. released under the gpl v3. see license.txt for details. 2 | namespace TeaTime 3 | { 4 | /// 5 | /// The TeaTime namespace provides classes to read and write TeaFiles. TeaFiles 6 | /// are a simple and efficient way to store time series in flat files. 7 | /// 8 | /// 9 | /// is used to create, write and read TeaFiles 10 | /// 11 | /// 12 | /// struct Tick // the time series item type 13 | /// { 14 | /// public DateTime Time; 15 | /// public double Price; 16 | /// public int Volume; 17 | /// } 18 | /// 19 | /// // create file and write some values 20 | /// using (var tf = TeaFile<Tick>.Create("gold.tea")) 21 | /// { 22 | /// tf.Write(new Tick { Price = 5, Time = DateTime.Now, Volume = 700 }); 23 | /// tf.Write(new Tick { Price = 15, Time = DateTime.Now.AddHours(1), Volume = 1700 }); 24 | /// } 25 | /// 26 | /// // read typed 27 | /// using (var tf = TeaFile<Tick>.OpenRead("gold.tea")) 28 | /// { 29 | /// Tick value = tf.Read(); 30 | /// Console.WriteLine(value); 31 | /// } 32 | /// 33 | /// 34 | /// If the type of items stored in a TeaFile is unknown, the file can still be opened using the non generic class . 35 | /// 36 | /// read untyped - we know nothing about the type of item in the file 37 | /// 38 | /// using (var tf = TeaFile.OpenRead("gold.tea")) 39 | /// { 40 | /// foreach(Item item in tf.Items) 41 | /// { 42 | /// Console.WriteLine(tf.Description.ItemDescription.GetNameValueString(item)); 43 | /// } 44 | /// } 45 | /// 46 | /// // output: 47 | /// output: 48 | /// Price=5 Time=20.8.2011 23:50 49 | /// Price=15 Time=21.8.2011 00:50 50 | /// 51 | /// 52 | /// If possible, typed reading is preferred, as it is much more performant and convient. Untyped reading should be used otherwise. 53 | /// Tools like TeaShell that present arbitrary TeaFiles, not known at compile time, 54 | /// use untyped reading.

55 | ///
56 | static class NamespaceDoc 57 | { 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Usage/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using TeaTime; 7 | 8 | namespace ACME.Examples 9 | { 10 | // define the item type of a TeaFile 11 | struct Tick 12 | { 13 | public Time Time; 14 | public double Price; 15 | 16 | public override string ToString() 17 | { 18 | return Time + " " + Price; 19 | } 20 | } 21 | 22 | class Program 23 | { 24 | static void Main() 25 | { 26 | // clean up from previous runs 27 | File.Delete("acme.tea"); 28 | 29 | // create file 30 | using (var tf = TeaFile.Create("acme.tea")) 31 | { 32 | tf.Write(new Tick { Time = DateTime.Now, Price = 12 }); 33 | tf.Write(new Tick { Time = DateTime.Now.AddDays(1), Price = 15 }); 34 | tf.Write(new Tick { Time = DateTime.Now.AddDays(2), Price = 18 }); 35 | } 36 | 37 | // read file 38 | using (var tf = TeaFile.OpenRead("acme.tea")) 39 | { 40 | foreach (var tick in tf.Items) 41 | { 42 | Console.WriteLine(tick); 43 | } 44 | tf.Write(new Tick { Time = DateTime.Now.AddDays(1), Price = 15 }); 45 | tf.Write(new Tick { Time = DateTime.Now.AddDays(2), Price = 18 }); 46 | } 47 | 48 | // create file with description 49 | File.Delete("acme.tea"); 50 | using (var tf = TeaFile.Create("acme.tea", 51 | "this file holds acme prices", 52 | NameValueCollection.From("decimals", 2, "datafeed","bloomfield"))) 53 | { 54 | tf.Write(new Tick { Time = DateTime.Now, Price = 12 }); 55 | tf.Write(new Tick { Time = DateTime.Now.AddDays(1), Price = 15 }); 56 | tf.Write(new Tick { Time = DateTime.Now.AddDays(2), Price = 18 }); 57 | } 58 | 59 | // get the file description by filename 60 | using (var tf = TeaFile.OpenRead("acme.tea")) 61 | { 62 | Console.WriteLine(tf.Description.ContentDescription); 63 | Console.WriteLine(tf.Description.NameValues.GetValue("decimals")); 64 | Console.WriteLine(tf.Description.NameValues.GetValue("datafeed")); 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Usage/Usage.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | x86 6 | 8.0.30703 7 | 2.0 8 | {7F882401-AA6D-443F-8FA4-E4AF8C3E5CB2} 9 | Exe 10 | Properties 11 | ACME.Examples 12 | Usage 13 | v4.7.2 14 | 15 | 16 | 512 17 | 18 | 19 | true 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | full 23 | AnyCPU 24 | bin\Debug\Usage.exe.CodeAnalysisLog.xml 25 | true 26 | GlobalSuppressions.cs 27 | prompt 28 | MinimumRecommendedRules.ruleset 29 | ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets 30 | true 31 | ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules 32 | true 33 | false 34 | 35 | 36 | bin\Release\ 37 | TRACE 38 | true 39 | pdbonly 40 | AnyCPU 41 | bin\Release\Usage.exe.CodeAnalysisLog.xml 42 | true 43 | GlobalSuppressions.cs 44 | prompt 45 | MinimumRecommendedRules.ruleset 46 | ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets 47 | true 48 | ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules 49 | true 50 | false 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | {1CADE377-2821-480D-A0E7-34F224886AF6} 67 | TeaFiles 68 | 69 | 70 | 71 | 72 | 73 | 74 | 81 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | # BASED ON THIS DECENT DESCRIPTION 2 | # https://ronaldbosma.github.io/blog/2019/09/03/using-multi-stage-yaml-pipeline-to-create-and-publish-nuget-packages/ 3 | 4 | trigger: 5 | - master 6 | 7 | stages: 8 | 9 | - stage: 'Build' 10 | variables: 11 | buildConfiguration: 'Release' 12 | 13 | jobs: 14 | - job: 15 | pool: 16 | vmImage: 'ubuntu-latest' 17 | 18 | workspace: 19 | clean: all 20 | 21 | steps: 22 | - task: UseDotNet@2 23 | displayName: 'Use .NET Core sdk' 24 | inputs: 25 | packageType: sdk 26 | version: 2.2.x 27 | installationPath: $(Agent.ToolsDirectory)/dotnet 28 | 29 | - task: DotNetCoreCLI@2 30 | displayName: "NuGet Restore" 31 | inputs: 32 | command: restore 33 | projects: '**/TeaFiles.csproj' 34 | 35 | - task: DotNetCoreCLI@2 36 | displayName: "Build Solution" 37 | inputs: 38 | command: build 39 | projects: '**/TeaFiles.csproj' 40 | arguments: '--configuration $(buildConfiguration)' 41 | 42 | - task: DotNetCoreCLI@2 43 | displayName: 'Create NuGet Package - Release Version' 44 | inputs: 45 | command: pack 46 | packagesToPack: '**/TeaFiles.csproj' 47 | packDirectory: '$(Build.ArtifactStagingDirectory)/packages/releases' 48 | arguments: '--configuration $(buildConfiguration)' 49 | nobuild: true 50 | 51 | - task: DotNetCoreCLI@2 52 | displayName: 'Create NuGet Package - Prerelease Version' 53 | inputs: 54 | command: pack 55 | packagesToPack: '**/TeaFiles.csproj' 56 | buildProperties: 'VersionSuffix="$(Build.BuildNumber)"' 57 | packDirectory: '$(Build.ArtifactStagingDirectory)/packages/prereleases' 58 | arguments: '--configuration $(buildConfiguration)' 59 | 60 | - publish: '$(Build.ArtifactStagingDirectory)/packages' 61 | artifact: 'packages' 62 | 63 | - stage: 'PublishPrereleaseNuGetPackage' 64 | displayName: 'Publish Prerelease NuGet Package' 65 | dependsOn: 'Build' 66 | condition: succeeded() 67 | jobs: 68 | - job: 69 | pool: 70 | vmImage: 'ubuntu-latest' 71 | 72 | steps: 73 | - checkout: none 74 | 75 | - download: current 76 | artifact: 'packages' 77 | 78 | - task: NuGetCommand@2 79 | displayName: 'Push NuGet Package' 80 | inputs: 81 | command: 'push' 82 | packagesToPush: '$(Pipeline.Workspace)/packages/prereleases/*.nupkg' 83 | nuGetFeedType: 'internal' 84 | publishVstsFeed: 'Discretely' 85 | -------------------------------------------------------------------------------- /normal32.testsettings: -------------------------------------------------------------------------------- 1 |  2 | 3 | These are default test settings for a local test run. 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 |
19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | --------------------------------------------------------------------------------