root / trunk / Pithos.Network / TreeHash.cs @ a27aa447
History | View | Annotate | Download (5.3 kB)
1 |
using System; |
---|---|
2 |
using System.Collections.Concurrent; |
3 |
using System.Collections.Generic; |
4 |
using System.IO; |
5 |
using System.Text; |
6 |
using System.Threading.Tasks; |
7 |
using Newtonsoft.Json; |
8 |
using System.Linq; |
9 |
using Newtonsoft.Json.Linq; |
10 |
|
11 |
namespace Pithos.Network |
12 |
{ |
13 |
public class TreeHash |
14 |
{ |
15 |
private const string DEFAULT_HASH_ALGORITHM = "sha256"; |
16 |
private const int DEFAULT_BLOCK_SIZE = 4*1024*1024; |
17 |
public string BlockHash { get; set; } |
18 |
public int BlockSize { get; set; } |
19 |
public long Bytes { get; set; } |
20 |
|
21 |
public Guid FileId { get; set; } |
22 |
|
23 |
private readonly Lazy<byte[]> _topHash; |
24 |
public byte[] TopHash |
25 |
{ |
26 |
get { return _topHash.Value; } |
27 |
} |
28 |
|
29 |
private IList<byte[]> _hashes; |
30 |
public IList<byte[]> Hashes |
31 |
{ |
32 |
get { return _hashes; } |
33 |
set |
34 |
{ |
35 |
_hashes = value; |
36 |
_topHash.Force(); |
37 |
} |
38 |
} |
39 |
|
40 |
public TreeHash(string algorithm) |
41 |
{ |
42 |
BlockHash = algorithm; |
43 |
_topHash = new Lazy<byte[]>(() => |
44 |
{ |
45 |
// |
46 |
//Cast the hashes to an IList or create a new list out of the hashes |
47 |
var hashMap = (_hashes as IList<byte[]>)??_hashes.ToList(); |
48 |
return Signature.CalculateTopHash(hashMap, BlockHash); |
49 |
}); |
50 |
} |
51 |
|
52 |
//Returns a Json representation of the hashes, as required by Pithos |
53 |
public string ToJson() |
54 |
{ |
55 |
var value = new JObject(); |
56 |
//We avoid using JObject's dynamic features because they use exceptions to detect new properties. |
57 |
value["block_hash"] = BlockHash; |
58 |
//Create a string array for all the hashes |
59 |
|
60 |
string[] hashes=null ; |
61 |
if (Hashes!=null) |
62 |
hashes= GetHashesAsStrings(); |
63 |
value["hashes"]= new JArray(hashes); |
64 |
value["block_size"] = BlockSize; |
65 |
value["bytes"] = Bytes; |
66 |
|
67 |
var json = JsonConvert.SerializeObject(value, Formatting.None); |
68 |
return json; |
69 |
} |
70 |
|
71 |
private string[] _stringHashes; |
72 |
//Returns the hashes as an array of hash strings. Used for serialization to Json |
73 |
public string[] GetHashesAsStrings() |
74 |
{ |
75 |
return _stringHashes |
76 |
?? (_stringHashes = Hashes.Select(hash => hash.ToHashString()).ToArray()); |
77 |
} |
78 |
|
79 |
ConcurrentDictionary<string, int> _blocks; |
80 |
//Retruns the hashes as a dictionary to the block location. Used to locate blocks |
81 |
public IDictionary<string,int> HashDictionary |
82 |
{ |
83 |
get |
84 |
{ |
85 |
Func<ConcurrentDictionary<string, int>> blocksInit = () => |
86 |
{ |
87 |
var blocks = |
88 |
new ConcurrentDictionary<string, int>(); |
89 |
|
90 |
var blockIndex = 0; |
91 |
foreach (var hash in this.Hashes) |
92 |
{ |
93 |
blocks[hash.ToHashString()] = blockIndex++; |
94 |
} |
95 |
return blocks; |
96 |
}; |
97 |
|
98 |
return _blocks ?? (_blocks = blocksInit()); |
99 |
} |
100 |
} |
101 |
|
102 |
//Saves the Json representation to a file |
103 |
public Task Save(string filePath) |
104 |
{ |
105 |
var fileName = FileId.ToString("N"); |
106 |
var path = Path.Combine(filePath, fileName); |
107 |
if (!Directory.Exists(filePath)) |
108 |
Directory.CreateDirectory(filePath); |
109 |
|
110 |
return Task.Factory |
111 |
.StartNew<string>(ToJson) |
112 |
.ContinueWith(jsonTask=> |
113 |
FileAsync.WriteAllText(path, jsonTask.Result)).Unwrap(); |
114 |
} |
115 |
|
116 |
public static Task<TreeHash> LoadTreeHash(string dataPath,Guid fileId) |
117 |
{ |
118 |
var fileName = fileId.ToString("N"); |
119 |
var path = Path.Combine(dataPath, fileName); |
120 |
return FileAsync.ReadAllText(path).ContinueWith(loadTask => |
121 |
{ |
122 |
var json = loadTask.Result; |
123 |
var treeHash = Parse(json); |
124 |
treeHash.FileId = fileId; |
125 |
return treeHash; |
126 |
}); |
127 |
} |
128 |
|
129 |
public static TreeHash Empty = new TreeHash(DEFAULT_HASH_ALGORITHM) |
130 |
{ |
131 |
BlockSize = DEFAULT_BLOCK_SIZE, |
132 |
Bytes = 0 |
133 |
}; |
134 |
|
135 |
//Parse a json string and return a TreeHash |
136 |
//Returns an empty TreeHash if the string is null or empty |
137 |
public static TreeHash Parse(string json) |
138 |
{ |
139 |
if (String.IsNullOrWhiteSpace(json)) |
140 |
return Empty; |
141 |
|
142 |
var value = JsonConvert.DeserializeObject<JObject>(json); |
143 |
|
144 |
var blockHash = (string) value["block_hash"]; |
145 |
var size = value.Value<int>("block_size"); |
146 |
var bytes = value.Value<long>("bytes"); |
147 |
var hashes = value.Value<JArray>("hashes"); |
148 |
var hashValues = from JToken token in hashes |
149 |
select token.Value<string>().ToBytes(); |
150 |
|
151 |
var treeHash = new TreeHash(blockHash) |
152 |
{ |
153 |
BlockSize = size, |
154 |
Hashes = hashValues.ToList(), |
155 |
Bytes = bytes |
156 |
}; |
157 |
return treeHash; |
158 |
} |
159 |
} |
160 |
} |