Statistics
| Branch: | Revision:

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
}