PutHashMap converted to HttpClient
[pithos-ms-client] / trunk / Pithos.Network / TreeHash.cs
1 #region\r
2 /* -----------------------------------------------------------------------\r
3  * <copyright file="TreeHash.cs" company="GRNet">\r
4  * \r
5  * Copyright 2011-2012 GRNET S.A. All rights reserved.\r
6  *\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
10  *\r
11  *   1. Redistributions of source code must retain the above\r
12  *      copyright notice, this list of conditions and the following\r
13  *      disclaimer.\r
14  *\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
19  *\r
20  *\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
33  *\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
38  * </copyright>\r
39  * -----------------------------------------------------------------------\r
40  */\r
41 #endregion\r
42 using System;\r
43 using System.Collections.Concurrent;\r
44 using System.Collections.Generic;\r
45 using System.Diagnostics.Contracts;\r
46 using System.IO;\r
47 using System.Text;\r
48 using System.Threading.Tasks;\r
49 \r
50 using System.Linq;\r
51 using Newtonsoft.Json;\r
52 using Newtonsoft.Json.Linq;\r
53 \r
54 namespace Pithos.Network\r
55 {\r
56     public class TreeHash\r
57     {\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
62         \r
63         private long _bytes;\r
64         public long Bytes\r
65         {\r
66             get\r
67             {\r
68                 Contract.Ensures(Contract.Result<long>() >= 0);\r
69                 return _bytes;\r
70             }\r
71             set\r
72             {\r
73                 if (value<0)\r
74                     throw new ArgumentOutOfRangeException("Bytes");\r
75                 Contract.Requires(value >= 0);\r
76                 Contract.EndContractBlock();\r
77                 \r
78                 _bytes = value;\r
79             }\r
80         }\r
81         \r
82 \r
83 \r
84         public Guid FileId { get; set; }\r
85 \r
86         private readonly Lazy<byte[]> _topHash;        \r
87         public byte[] TopHash\r
88         {\r
89             get { return _topHash.Value; }\r
90         }\r
91 \r
92         private IList<byte[]> _hashes=new List<byte[]>();\r
93 \r
94         public IList<byte[]> Hashes\r
95         {\r
96             get { return _hashes; }\r
97             set\r
98             {\r
99                 _hashes = value;\r
100                 _topHash.Force();\r
101             }\r
102         }\r
103 \r
104         [ContractInvariantMethod]\r
105         private void Invariants()\r
106         {\r
107             Contract.Invariant(_bytes>=0);\r
108         }\r
109         \r
110 \r
111        public TreeHash(string algorithm)\r
112         {\r
113             BlockHash = algorithm;            \r
114             _topHash = new Lazy<byte[]>(() =>\r
115             {\r
116                 //\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
120             });\r
121         }\r
122 \r
123         //Returns a Json representation of the hashes, as required by Pithos\r
124         public string ToJson()\r
125         {\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
130             \r
131             string[] hashes=null ;\r
132             if (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
137             \r
138             var json = JsonConvert.SerializeObject(value, Formatting.None);\r
139             return json;\r
140         }\r
141 \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
145         {\r
146             return _stringHashes \r
147                 ?? (_stringHashes = Hashes.Select(hash => hash.ToHashString()).ToArray());\r
148         }\r
149 \r
150         ConcurrentDictionary<string, long> _blocks;\r
151         \r
152         //Retruns the hashes as a dictionary to the block location. Used to locate blocks\r
153         public IDictionary<string,long> HashDictionary\r
154         {\r
155             get\r
156             {\r
157                 Func<ConcurrentDictionary<string, long>> blocksInit = () =>\r
158                 {\r
159                     var blocks =\r
160                         new ConcurrentDictionary<string, long>();\r
161                     if (Hashes == null)\r
162                         return blocks;\r
163 \r
164                     var blockIndex = 0;\r
165                     foreach (var hash in this.Hashes)\r
166                     {\r
167                         blocks[hash.ToHashString()] = blockIndex++;\r
168                     }\r
169                     return blocks;\r
170                 };\r
171 \r
172                 return _blocks ?? (_blocks = blocksInit());\r
173             }\r
174         }\r
175 \r
176         //private string _md5=Signature.MD5_EMPTY;\r
177         //public string MD5\r
178         //{\r
179         //    get { return _md5; }\r
180         //    set { _md5 = value; }\r
181         //}\r
182 \r
183         //Saves the Json representation to a file\r
184         public async Task Save(string filePath)\r
185         {\r
186             if (String.IsNullOrWhiteSpace(filePath))\r
187                 throw new ArgumentNullException("filePath");\r
188 \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
193 \r
194             var json = await TaskEx.Run(() => ToJson()).ConfigureAwait(false);\r
195             await FileAsync.WriteAllText(path, json).ConfigureAwait(false);\r
196         }\r
197 \r
198         public static async Task<TreeHash> LoadTreeHash(string dataPath,Guid fileId)\r
199         {\r
200             var fileName = fileId.ToString("N");\r
201             var path = Path.Combine(dataPath, fileName);\r
202 \r
203             var json = await FileAsync.ReadAllText(path).ConfigureAwait(false);\r
204             var treeHash = Parse(json);\r
205             treeHash.FileId = fileId;\r
206             return treeHash;\r
207         }\r
208 \r
209         public static TreeHash Empty = new TreeHash(DEFAULT_HASH_ALGORITHM)\r
210         {\r
211             BlockSize = DEFAULT_BLOCK_SIZE,\r
212             Bytes = 0\r
213         };\r
214 \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
218         {\r
219             if (String.IsNullOrWhiteSpace(json))\r
220                 return Empty;            \r
221 \r
222             var value = JsonConvert.DeserializeObject<JObject>(json);\r
223             if (value==null)\r
224                 throw new ArgumentException("The json parameter doesn't contain any json data","json");\r
225             Contract.Assume(value!=null);\r
226 \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
233 \r
234             var treeHash = new TreeHash(blockHash)\r
235                                {\r
236                                    BlockSize = size,\r
237                                    Hashes = hashValues.ToList(),\r
238                                    Bytes = bytes\r
239                                };\r
240             return treeHash;\r
241         }\r
242     }\r
243 }