root / trunk / Pithos.Network / TreeHash.cs @ 2115e2a5
History | View | Annotate | Download (8.3 kB)
1 |
#region |
---|---|
2 |
/* ----------------------------------------------------------------------- |
3 |
* <copyright file="TreeHash.cs" company="GRNet"> |
4 |
* |
5 |
* Copyright 2011-2012 GRNET S.A. All rights reserved. |
6 |
* |
7 |
* Redistribution and use in source and binary forms, with or |
8 |
* without modification, are permitted provided that the following |
9 |
* conditions are met: |
10 |
* |
11 |
* 1. Redistributions of source code must retain the above |
12 |
* copyright notice, this list of conditions and the following |
13 |
* disclaimer. |
14 |
* |
15 |
* 2. Redistributions in binary form must reproduce the above |
16 |
* copyright notice, this list of conditions and the following |
17 |
* disclaimer in the documentation and/or other materials |
18 |
* provided with the distribution. |
19 |
* |
20 |
* |
21 |
* THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS |
22 |
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
23 |
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
24 |
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR |
25 |
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
26 |
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
27 |
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
28 |
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
29 |
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
30 |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
31 |
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
32 |
* POSSIBILITY OF SUCH DAMAGE. |
33 |
* |
34 |
* The views and conclusions contained in the software and |
35 |
* documentation are those of the authors and should not be |
36 |
* interpreted as representing official policies, either expressed |
37 |
* or implied, of GRNET S.A. |
38 |
* </copyright> |
39 |
* ----------------------------------------------------------------------- |
40 |
*/ |
41 |
#endregion |
42 |
using System; |
43 |
using System.Collections.Concurrent; |
44 |
using System.Collections.Generic; |
45 |
using System.Diagnostics.Contracts; |
46 |
using System.IO; |
47 |
using System.Text; |
48 |
using System.Threading.Tasks; |
49 |
|
50 |
using System.Linq; |
51 |
using Newtonsoft.Json; |
52 |
using Newtonsoft.Json.Linq; |
53 |
|
54 |
namespace Pithos.Network |
55 |
{ |
56 |
public class TreeHash |
57 |
{ |
58 |
public const string DEFAULT_HASH_ALGORITHM = "sha256"; |
59 |
public const long DEFAULT_BLOCK_SIZE = 4*1024*1024; |
60 |
public string BlockHash { get; set; } |
61 |
public long BlockSize { get; set; } |
62 |
|
63 |
private long _bytes; |
64 |
public long Bytes |
65 |
{ |
66 |
get |
67 |
{ |
68 |
Contract.Ensures(Contract.Result<long>() >= 0); |
69 |
return _bytes; |
70 |
} |
71 |
set |
72 |
{ |
73 |
Contract.Requires<ArgumentOutOfRangeException>(value>=0,"Bytes"); |
74 |
Contract.EndContractBlock(); |
75 |
|
76 |
_bytes = value; |
77 |
} |
78 |
} |
79 |
|
80 |
|
81 |
|
82 |
public Guid FileId { get; set; } |
83 |
|
84 |
private readonly Lazy<byte[]> _topHash; |
85 |
public byte[] TopHash |
86 |
{ |
87 |
get { return _topHash.Value; } |
88 |
} |
89 |
|
90 |
private IList<byte[]> _hashes=new List<byte[]>(); |
91 |
|
92 |
public IList<byte[]> Hashes |
93 |
{ |
94 |
get { return _hashes; } |
95 |
set |
96 |
{ |
97 |
_hashes = value; |
98 |
_topHash.Force(); |
99 |
} |
100 |
} |
101 |
|
102 |
[ContractInvariantMethod] |
103 |
private void Invariants() |
104 |
{ |
105 |
Contract.Invariant(_bytes>=0); |
106 |
} |
107 |
|
108 |
|
109 |
public TreeHash(string algorithm) |
110 |
{ |
111 |
BlockHash = algorithm; |
112 |
_topHash = new Lazy<byte[]>(() => |
113 |
{ |
114 |
// |
115 |
//Cast the hashes to an IList or create a new list out of the hashes |
116 |
var hashMap = (_hashes as IList<byte[]>)??_hashes.ToList(); |
117 |
return Signature.CalculateTopHash(hashMap, BlockHash); |
118 |
}); |
119 |
} |
120 |
|
121 |
//Returns a Json representation of the hashes, as required by Pithos |
122 |
public string ToJson() |
123 |
{ |
124 |
var value = new JObject(); |
125 |
//We avoid using JObject's dynamic features because they use exceptions to detect new properties. |
126 |
value["block_hash"] = BlockHash; |
127 |
//Create a string array for all the hashes |
128 |
|
129 |
string[] hashes=null ; |
130 |
if (Hashes!=null) |
131 |
hashes= GetHashesAsStrings(); |
132 |
value["hashes"]= new JArray(hashes); |
133 |
value["block_size"] = BlockSize; |
134 |
value["bytes"] = Bytes; |
135 |
|
136 |
var json = JsonConvert.SerializeObject(value, Formatting.None); |
137 |
return json; |
138 |
} |
139 |
|
140 |
private string[] _stringHashes; |
141 |
//Returns the hashes as an array of hash strings. Used for serialization to Json |
142 |
public string[] GetHashesAsStrings() |
143 |
{ |
144 |
return _stringHashes |
145 |
?? (_stringHashes = Hashes.Select(hash => hash.ToHashString()).ToArray()); |
146 |
} |
147 |
|
148 |
ConcurrentDictionary<string, long> _blocks; |
149 |
|
150 |
//Retruns the hashes as a dictionary to the block location. Used to locate blocks |
151 |
public IDictionary<string,long> HashDictionary |
152 |
{ |
153 |
get |
154 |
{ |
155 |
Func<ConcurrentDictionary<string, long>> blocksInit = () => |
156 |
{ |
157 |
var blocks = |
158 |
new ConcurrentDictionary<string, long>(); |
159 |
if (Hashes == null) |
160 |
return blocks; |
161 |
|
162 |
var blockIndex = 0; |
163 |
foreach (var hash in this.Hashes) |
164 |
{ |
165 |
blocks[hash.ToHashString()] = blockIndex++; |
166 |
} |
167 |
return blocks; |
168 |
}; |
169 |
|
170 |
return _blocks ?? (_blocks = blocksInit()); |
171 |
} |
172 |
} |
173 |
|
174 |
//private string _md5=Signature.MD5_EMPTY; |
175 |
//public string MD5 |
176 |
//{ |
177 |
// get { return _md5; } |
178 |
// set { _md5 = value; } |
179 |
//} |
180 |
|
181 |
//Saves the Json representation to a file |
182 |
public async Task Save(string filePath) |
183 |
{ |
184 |
if (String.IsNullOrWhiteSpace(filePath)) |
185 |
throw new ArgumentNullException("filePath"); |
186 |
|
187 |
var fileName = FileId.ToString("N"); |
188 |
var path = Path.Combine(filePath, fileName); |
189 |
if (!Directory.Exists(filePath)) |
190 |
Directory.CreateDirectory(filePath); |
191 |
|
192 |
var json = await TaskEx.Run(() => ToJson()).ConfigureAwait(false); |
193 |
await FileAsync.WriteAllText(path, json).ConfigureAwait(false); |
194 |
} |
195 |
|
196 |
public static async Task<TreeHash> LoadTreeHash(string dataPath,Guid fileId) |
197 |
{ |
198 |
var fileName = fileId.ToString("N"); |
199 |
var path = Path.Combine(dataPath, fileName); |
200 |
|
201 |
var json = await FileAsync.ReadAllText(path).ConfigureAwait(false); |
202 |
var treeHash = Parse(json); |
203 |
treeHash.FileId = fileId; |
204 |
return treeHash; |
205 |
} |
206 |
|
207 |
public static TreeHash Empty = new TreeHash(DEFAULT_HASH_ALGORITHM) |
208 |
{ |
209 |
BlockSize = DEFAULT_BLOCK_SIZE, |
210 |
Bytes = 0 |
211 |
}; |
212 |
|
213 |
//Parse a json string and return a TreeHash |
214 |
//Returns an empty TreeHash if the string is null or empty |
215 |
public static TreeHash Parse(string json) |
216 |
{ |
217 |
if (String.IsNullOrWhiteSpace(json)) |
218 |
return Empty; |
219 |
|
220 |
var value = JsonConvert.DeserializeObject<JObject>(json); |
221 |
if (value==null) |
222 |
throw new ArgumentException("The json parameter doesn't contain any json data","json"); |
223 |
Contract.Assume(value!=null); |
224 |
|
225 |
var blockHash = (string) value["block_hash"]; |
226 |
var size = value.Value<int>("block_size"); |
227 |
var bytes = value.Value<long>("bytes"); |
228 |
var hashes = value.Value<JArray>("hashes"); |
229 |
var hashValues = from JToken token in hashes |
230 |
select token.Value<string>().ToBytes(); |
231 |
|
232 |
var treeHash = new TreeHash(blockHash) |
233 |
{ |
234 |
BlockSize = size, |
235 |
Hashes = hashValues.ToList(), |
236 |
Bytes = bytes |
237 |
}; |
238 |
return treeHash; |
239 |
} |
240 |
} |
241 |
} |