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 async 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);
146 var json=await TaskEx.Run(()=>ToJson());
147 await FileAsync.WriteAllText(path, json);
150 public static async Task<TreeHash> LoadTreeHash(string dataPath,Guid fileId)
152 var fileName = fileId.ToString("N");
153 var path = Path.Combine(dataPath, fileName);
155 var json=await FileAsync.ReadAllText(path);
156 var treeHash = Parse(json);
157 treeHash.FileId = fileId;
161 public static TreeHash Empty = new TreeHash(DEFAULT_HASH_ALGORITHM)
163 BlockSize = DEFAULT_BLOCK_SIZE,
167 //Parse a json string and return a TreeHash
168 //Returns an empty TreeHash if the string is null or empty
169 public static TreeHash Parse(string json)
171 if (String.IsNullOrWhiteSpace(json))
174 var value = JsonConvert.DeserializeObject<JObject>(json);
176 throw new ArgumentException("The json parameter doesn't contain any json data","json");
177 Contract.Assume(value!=null);
179 var blockHash = (string) value["block_hash"];
180 var size = value.Value<int>("block_size");
181 var bytes = value.Value<long>("bytes");
182 var hashes = value.Value<JArray>("hashes");
183 var hashValues = from JToken token in hashes
184 select token.Value<string>().ToBytes();
186 var treeHash = new TreeHash(blockHash)
189 Hashes = hashValues.ToList(),