2 using System.Collections.Concurrent;
3 using System.Collections.Generic;
4 using System.Diagnostics.Contracts;
7 using System.Threading.Tasks;
10 using Newtonsoft.Json.Linq;
12 namespace Pithos.Network
16 private const string DEFAULT_HASH_ALGORITHM = "sha256";
17 private const int DEFAULT_BLOCK_SIZE = 4*1024*1024;
18 public string BlockHash { get; set; }
19 public int BlockSize { get; set; }
26 Contract.Ensures(Contract.Result<long>() >= 0);
32 throw new ArgumentOutOfRangeException("Bytes");
33 Contract.Requires(value >= 0);
34 Contract.EndContractBlock();
42 public Guid FileId { get; set; }
44 private readonly Lazy<byte[]> _topHash;
47 get { return _topHash.Value; }
50 private IList<byte[]> _hashes;
52 public IList<byte[]> Hashes
54 get { return _hashes; }
62 [ContractInvariantMethod]
63 private void Invariants()
65 Contract.Invariant(_bytes>=0);
69 public TreeHash(string algorithm)
71 BlockHash = algorithm;
72 _topHash = new Lazy<byte[]>(() =>
75 //Cast the hashes to an IList or create a new list out of the hashes
76 var hashMap = (_hashes as IList<byte[]>)??_hashes.ToList();
77 return Signature.CalculateTopHash(hashMap, BlockHash);
81 //Returns a Json representation of the hashes, as required by Pithos
82 public string ToJson()
84 var value = new JObject();
85 //We avoid using JObject's dynamic features because they use exceptions to detect new properties.
86 value["block_hash"] = BlockHash;
87 //Create a string array for all the hashes
89 string[] hashes=null ;
91 hashes= GetHashesAsStrings();
92 value["hashes"]= new JArray(hashes);
93 value["block_size"] = BlockSize;
94 value["bytes"] = Bytes;
96 var json = JsonConvert.SerializeObject(value, Formatting.None);
100 private string[] _stringHashes;
101 //Returns the hashes as an array of hash strings. Used for serialization to Json
102 public string[] GetHashesAsStrings()
105 ?? (_stringHashes = Hashes.Select(hash => hash.ToHashString()).ToArray());
108 ConcurrentDictionary<string, int> _blocks;
109 //Retruns the hashes as a dictionary to the block location. Used to locate blocks
110 public IDictionary<string,int> HashDictionary
114 Func<ConcurrentDictionary<string, int>> blocksInit = () =>
117 new ConcurrentDictionary<string, int>();
122 foreach (var hash in this.Hashes)
124 blocks[hash.ToHashString()] = blockIndex++;
129 return _blocks ?? (_blocks = blocksInit());
133 //Saves the Json representation to a file
134 public Task Save(string filePath)
136 if (String.IsNullOrWhiteSpace(filePath))
137 throw new ArgumentNullException("filePath");
138 Contract.EndContractBlock();
140 var fileName = FileId.ToString("N");
141 var path = Path.Combine(filePath, fileName);
142 if (!Directory.Exists(filePath))
143 Directory.CreateDirectory(filePath);
146 .StartNew<string>(ToJson)
147 .ContinueWith(jsonTask=>
148 FileAsync.WriteAllText(path, jsonTask.Result)).Unwrap();
151 public static Task<TreeHash> LoadTreeHash(string dataPath,Guid fileId)
153 var fileName = fileId.ToString("N");
154 var path = Path.Combine(dataPath, fileName);
155 return FileAsync.ReadAllText(path).ContinueWith(loadTask =>
157 var json = loadTask.Result;
158 var treeHash = Parse(json);
159 treeHash.FileId = fileId;
164 public static TreeHash Empty = new TreeHash(DEFAULT_HASH_ALGORITHM)
166 BlockSize = DEFAULT_BLOCK_SIZE,
170 //Parse a json string and return a TreeHash
171 //Returns an empty TreeHash if the string is null or empty
172 public static TreeHash Parse(string json)
174 if (String.IsNullOrWhiteSpace(json))
177 var value = JsonConvert.DeserializeObject<JObject>(json);
179 throw new ArgumentException("The json parameter doesn't contain any json data","json");
180 Contract.Assume(value!=null);
182 var blockHash = (string) value["block_hash"];
183 var size = value.Value<int>("block_size");
184 var bytes = value.Value<long>("bytes");
185 var hashes = value.Value<JArray>("hashes");
186 var hashValues = from JToken token in hashes
187 select token.Value<string>().ToBytes();
189 var treeHash = new TreeHash(blockHash)
192 Hashes = hashValues.ToList(),