2 /* -----------------------------------------------------------------------
\r
3 * <copyright file="TreeHash.cs" company="GRNet">
\r
5 * Copyright 2011-2012 GRNET S.A. All rights reserved.
\r
7 * Redistribution and use in source and binary forms, with or
\r
8 * without modification, are permitted provided that the following
\r
9 * conditions are met:
\r
11 * 1. Redistributions of source code must retain the above
\r
12 * copyright notice, this list of conditions and the following
\r
15 * 2. Redistributions in binary form must reproduce the above
\r
16 * copyright notice, this list of conditions and the following
\r
17 * disclaimer in the documentation and/or other materials
\r
18 * provided with the distribution.
\r
21 * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
\r
22 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
\r
23 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
\r
24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
\r
25 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
\r
26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
\r
27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
\r
28 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
\r
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
\r
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
\r
31 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
\r
32 * POSSIBILITY OF SUCH DAMAGE.
\r
34 * The views and conclusions contained in the software and
\r
35 * documentation are those of the authors and should not be
\r
36 * interpreted as representing official policies, either expressed
\r
37 * or implied, of GRNET S.A.
\r
39 * -----------------------------------------------------------------------
\r
43 using System.Collections.Concurrent;
\r
44 using System.Collections.Generic;
\r
45 using System.Diagnostics.Contracts;
\r
48 using System.Threading.Tasks;
\r
51 using Newtonsoft.Json;
\r
52 using Newtonsoft.Json.Linq;
\r
54 namespace Pithos.Network
\r
56 public class TreeHash
\r
58 public const string DEFAULT_HASH_ALGORITHM = "sha256";
\r
59 public const long DEFAULT_BLOCK_SIZE = 4*1024*1024;
\r
60 public string BlockHash { get; set; }
\r
61 public long BlockSize { get; set; }
\r
63 private long _bytes;
\r
68 Contract.Ensures(Contract.Result<long>() >= 0);
\r
74 throw new ArgumentOutOfRangeException("Bytes");
\r
75 Contract.Requires(value >= 0);
\r
76 Contract.EndContractBlock();
\r
84 public Guid FileId { get; set; }
\r
86 private readonly Lazy<byte[]> _topHash;
\r
87 public byte[] TopHash
\r
89 get { return _topHash.Value; }
\r
92 private IList<byte[]> _hashes=new List<byte[]>();
\r
94 public IList<byte[]> Hashes
\r
96 get { return _hashes; }
\r
104 [ContractInvariantMethod]
\r
105 private void Invariants()
\r
107 Contract.Invariant(_bytes>=0);
\r
111 public TreeHash(string algorithm)
\r
113 BlockHash = algorithm;
\r
114 _topHash = new Lazy<byte[]>(() =>
\r
117 //Cast the hashes to an IList or create a new list out of the hashes
\r
118 var hashMap = (_hashes as IList<byte[]>)??_hashes.ToList();
\r
119 return Signature.CalculateTopHash(hashMap, BlockHash);
\r
123 //Returns a Json representation of the hashes, as required by Pithos
\r
124 public string ToJson()
\r
126 var value = new JObject();
\r
127 //We avoid using JObject's dynamic features because they use exceptions to detect new properties.
\r
128 value["block_hash"] = BlockHash;
\r
129 //Create a string array for all the hashes
\r
131 string[] hashes=null ;
\r
133 hashes= GetHashesAsStrings();
\r
134 value["hashes"]= new JArray(hashes);
\r
135 value["block_size"] = BlockSize;
\r
136 value["bytes"] = Bytes;
\r
138 var json = JsonConvert.SerializeObject(value, Formatting.None);
\r
142 private string[] _stringHashes;
\r
143 //Returns the hashes as an array of hash strings. Used for serialization to Json
\r
144 public string[] GetHashesAsStrings()
\r
146 return _stringHashes
\r
147 ?? (_stringHashes = Hashes.Select(hash => hash.ToHashString()).ToArray());
\r
150 ConcurrentDictionary<string, long> _blocks;
\r
152 //Retruns the hashes as a dictionary to the block location. Used to locate blocks
\r
153 public IDictionary<string,long> HashDictionary
\r
157 Func<ConcurrentDictionary<string, long>> blocksInit = () =>
\r
160 new ConcurrentDictionary<string, long>();
\r
161 if (Hashes == null)
\r
164 var blockIndex = 0;
\r
165 foreach (var hash in this.Hashes)
\r
167 blocks[hash.ToHashString()] = blockIndex++;
\r
172 return _blocks ?? (_blocks = blocksInit());
\r
176 private string _md5=Signature.MD5_EMPTY;
\r
179 get { return _md5; }
\r
180 set { _md5 = value; }
\r
183 //Saves the Json representation to a file
\r
184 public async Task Save(string filePath)
\r
186 if (String.IsNullOrWhiteSpace(filePath))
\r
187 throw new ArgumentNullException("filePath");
\r
189 var fileName = FileId.ToString("N");
\r
190 var path = Path.Combine(filePath, fileName);
\r
191 if (!Directory.Exists(filePath))
\r
192 Directory.CreateDirectory(filePath);
\r
194 var json = await TaskEx.Run(() => ToJson()).ConfigureAwait(false);
\r
195 await FileAsync.WriteAllText(path, json).ConfigureAwait(false);
\r
198 public static async Task<TreeHash> LoadTreeHash(string dataPath,Guid fileId)
\r
200 var fileName = fileId.ToString("N");
\r
201 var path = Path.Combine(dataPath, fileName);
\r
203 var json = await FileAsync.ReadAllText(path).ConfigureAwait(false);
\r
204 var treeHash = Parse(json);
\r
205 treeHash.FileId = fileId;
\r
209 public static TreeHash Empty = new TreeHash(DEFAULT_HASH_ALGORITHM)
\r
211 BlockSize = DEFAULT_BLOCK_SIZE,
\r
215 //Parse a json string and return a TreeHash
\r
216 //Returns an empty TreeHash if the string is null or empty
\r
217 public static TreeHash Parse(string json)
\r
219 if (String.IsNullOrWhiteSpace(json))
\r
222 var value = JsonConvert.DeserializeObject<JObject>(json);
\r
224 throw new ArgumentException("The json parameter doesn't contain any json data","json");
\r
225 Contract.Assume(value!=null);
\r
227 var blockHash = (string) value["block_hash"];
\r
228 var size = value.Value<int>("block_size");
\r
229 var bytes = value.Value<long>("bytes");
\r
230 var hashes = value.Value<JArray>("hashes");
\r
231 var hashValues = from JToken token in hashes
\r
232 select token.Value<string>().ToBytes();
\r
234 var treeHash = new TreeHash(blockHash)
\r
237 Hashes = hashValues.ToList(),
\r