Statistics
| Branch: | Revision:

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
}