2 using System.Collections.Concurrent;
3 using System.Collections.Generic;
4 using System.Diagnostics.Contracts;
7 using System.Threading.Tasks;
10 using Newtonsoft.Json;
11 using Newtonsoft.Json.Linq;
13 namespace Pithos.Network
17 private const string DEFAULT_HASH_ALGORITHM = "sha256";
18 private const int DEFAULT_BLOCK_SIZE = 4*1024*1024;
19 public string BlockHash { get; set; }
20 public int BlockSize { get; set; }
27 Contract.Ensures(Contract.Result<long>() >= 0);
33 throw new ArgumentOutOfRangeException("Bytes");
34 Contract.Requires(value >= 0);
35 Contract.EndContractBlock();
43 public Guid FileId { get; set; }
45 private readonly Lazy<byte[]> _topHash;
48 get { return _topHash.Value; }
51 private IList<byte[]> _hashes;
53 public IList<byte[]> Hashes
55 get { return _hashes; }
63 [ContractInvariantMethod]
64 private void Invariants()
66 Contract.Invariant(_bytes>=0);
70 public TreeHash(string algorithm)
72 BlockHash = algorithm;
73 _topHash = new Lazy<byte[]>(() =>
76 //Cast the hashes to an IList or create a new list out of the hashes
77 var hashMap = (_hashes as IList<byte[]>)??_hashes.ToList();
78 return Signature.CalculateTopHash(hashMap, BlockHash);
82 //Returns a Json representation of the hashes, as required by Pithos
83 public string ToJson()
85 var value = new JObject();
86 //We avoid using JObject's dynamic features because they use exceptions to detect new properties.
87 value["block_hash"] = BlockHash;
88 //Create a string array for all the hashes
90 string[] hashes=null ;
92 hashes= GetHashesAsStrings();
93 value["hashes"]= new JArray(hashes);
94 value["block_size"] = BlockSize;
95 value["bytes"] = Bytes;
97 var json = JsonConvert.SerializeObject(value, Formatting.None);
101 private string[] _stringHashes;
102 //Returns the hashes as an array of hash strings. Used for serialization to Json
103 public string[] GetHashesAsStrings()
106 ?? (_stringHashes = Hashes.Select(hash => hash.ToHashString()).ToArray());
109 ConcurrentDictionary<string, int> _blocks;
110 //Retruns the hashes as a dictionary to the block location. Used to locate blocks
111 public IDictionary<string,int> HashDictionary
115 Func<ConcurrentDictionary<string, int>> blocksInit = () =>
118 new ConcurrentDictionary<string, int>();
123 foreach (var hash in this.Hashes)
125 blocks[hash.ToHashString()] = blockIndex++;
130 return _blocks ?? (_blocks = blocksInit());
134 //Saves the Json representation to a file
135 public Task Save(string filePath)
137 if (String.IsNullOrWhiteSpace(filePath))
138 throw new ArgumentNullException("filePath");
139 Contract.EndContractBlock();
141 var fileName = FileId.ToString("N");
142 var path = Path.Combine(filePath, fileName);
143 if (!Directory.Exists(filePath))
144 Directory.CreateDirectory(filePath);
147 .StartNew<string>(ToJson)
148 .ContinueWith(jsonTask=>
149 FileAsync.WriteAllText(path, jsonTask.Result)).Unwrap();
152 public static Task<TreeHash> LoadTreeHash(string dataPath,Guid fileId)
154 var fileName = fileId.ToString("N");
155 var path = Path.Combine(dataPath, fileName);
156 return FileAsync.ReadAllText(path).ContinueWith(loadTask =>
158 var json = loadTask.Result;
159 var treeHash = Parse(json);
160 treeHash.FileId = fileId;
165 public static TreeHash Empty = new TreeHash(DEFAULT_HASH_ALGORITHM)
167 BlockSize = DEFAULT_BLOCK_SIZE,
171 //Parse a json string and return a TreeHash
172 //Returns an empty TreeHash if the string is null or empty
173 public static TreeHash Parse(string json)
175 if (String.IsNullOrWhiteSpace(json))
178 var value = JsonConvert.DeserializeObject<JObject>(json);
180 throw new ArgumentException("The json parameter doesn't contain any json data","json");
181 Contract.Assume(value!=null);
183 var blockHash = (string) value["block_hash"];
184 var size = value.Value<int>("block_size");
185 var bytes = value.Value<long>("bytes");
186 var hashes = value.Value<JArray>("hashes");
187 var hashValues = from JToken token in hashes
188 select token.Value<string>().ToBytes();
190 var treeHash = new TreeHash(blockHash)
193 Hashes = hashValues.ToList(),