Allow schema files that are missing checksums on the !!SCHEMAMATIC line.
[versaplex.git] / versaplexd / vxschemachecksums.cs
blob53aacd31e030e016f9f31a5197a100c5afbb4cd6
1 /*
2 * Versaplex:
3 * Copyright (C)2007-2008 Versabanq Innovations Inc. and contributors.
4 * See the included file named LICENSE for license information.
5 */
6 using System;
7 using System.IO;
8 using System.Linq;
9 using System.Collections.Generic;
10 using System.Security.Cryptography;
11 using Wv;
12 using Wv.Extensions;
14 // The checksums for a single database element (table, procedure, etc).
15 // Can have multiple checksum values - a table has one checksum per column,
16 // for instance.
17 // Note: This class is immutable.
18 // FIXME: Have separate type member?
19 internal class VxSchemaChecksum
21 readonly string _key;
22 public string key {
23 get { return _key; }
26 readonly IEnumerable<ulong> _checksums;
27 public IEnumerable<ulong> checksums {
28 get { return _checksums; }
31 public VxSchemaChecksum(VxSchemaChecksum copy)
33 _key = copy.key;
34 var list = new List<ulong>();
35 foreach (ulong sum in copy.checksums)
36 list.Add(sum);
37 _checksums = list;
40 public VxSchemaChecksum(string newkey)
42 _key = newkey;
43 _checksums = new List<ulong>();
46 public VxSchemaChecksum(string newkey, ulong newchecksum)
48 _key = newkey;
49 var list = new List<ulong>();
50 list.Add(newchecksum);
51 _checksums = list;
54 public VxSchemaChecksum(string newkey, IEnumerable<ulong> sumlist)
56 _key = newkey;
58 // Tables need to maintain their checksums in sorted order, as the
59 // columns might get out of order when the tables are otherwise
60 // identical.
61 string type, name;
62 VxSchemaChecksums.ParseKey(newkey, out type, out name);
63 if (type == "Table")
65 List<ulong> sorted = sumlist.ToList();
66 sorted.Sort();
67 _checksums = sorted;
69 else
70 _checksums = sumlist;
73 public string GetSumString()
75 List<string> l = new List<string>();
76 foreach (ulong sum in checksums)
77 l.Add("0x" + sum.ToString("x8"));
78 return l.join(" ");
81 // Write the checksum values to DBus
82 public void Write(WvDbusWriter writer)
84 writer.Write(key);
85 writer.WriteArray(8, _checksums, (w2, sum) => {
86 w2.Write(sum);
87 });
90 // Note: this is only safe to override because the class is immutable.
91 public override bool Equals(object other_obj)
93 if (other_obj == null)
94 return false;
96 if (!(other_obj is VxSchemaChecksum))
97 return false;
99 var other = (VxSchemaChecksum)other_obj;
101 if (this.key != other.key)
102 return false;
104 // FIXME: This can be replaced with Linq's SequenceEquals(), once
105 // we're using a version of mono that implements it (>= 1.9).
106 ulong[] mysums = this.checksums.ToArray();
107 ulong[] theirsums = other.checksums.ToArray();
109 if (mysums.Count() != theirsums.Count())
110 return false;
112 for (int i = 0; i < mysums.Count(); i++)
113 if (mysums[i] != theirsums[i])
114 return false;
116 return true;
119 public override int GetHashCode()
121 ulong xor = checksums.Aggregate((cur, next) => cur ^ next);
122 return ((int)xor ^ (int)(xor>>32));
125 // Given a string containing database sums, returns their parsed version.
126 // Leading "0x" prefixes are optional, though all numbers are assumed to
127 // be hex. The sums must be separated by spaces, e.g. "0xdeadbeef badf00d"
128 // Prints an error message and ignores any unparseable elements.
129 public static IEnumerable<ulong> ParseSumString(string dbsums)
131 return ParseSumString(dbsums, null);
134 // Acts just like ParseSumString(dbsums), but allows for providing
135 // some context for errors. If errctx isn't null, it's printed before any
136 // error messages produced. A suitable string might be "Error while
137 // reading file $filename: ".
138 public static IEnumerable<ulong> ParseSumString(string dbsums,
139 string errctx)
141 if (dbsums == null)
142 return new List<ulong>();
144 string[] sums = dbsums.Split(' ');
145 var sumlist = new List<ulong>();
146 foreach (string sumstr in sums)
148 // Ignore trailing spaces.
149 if (sumstr.Length == 0)
150 continue;
152 // C#'s hex parser doesn't like 0x prefixes.
153 string stripped = sumstr.ToLower();
154 if (stripped.StartsWith("0x"))
155 stripped = stripped.Remove(0, 2);
157 ulong longsum;
158 if (UInt64.TryParse(stripped,
159 System.Globalization.NumberStyles.HexNumber, null,
160 out longsum))
162 sumlist.Add(longsum);
164 else
166 WvLog log = new WvLog("ParseSumString");
167 string msg = wv.fmt("Failed to parse database sums '{0}' " +
168 "due to the malformed element '{1}'.\n",
169 dbsums, sumstr);
170 log.print("{0}{1}", errctx == null ? "" : errctx, msg);
173 return sumlist;
176 public static string GetDbusSignature()
178 return "sat";
182 // The checksum values for a set of database elements
183 internal class VxSchemaChecksums : Dictionary<string, VxSchemaChecksum>
185 public VxSchemaChecksums()
189 public VxSchemaChecksums(VxSchemaChecksums copy)
191 foreach (KeyValuePair<string,VxSchemaChecksum> p in copy)
192 this.Add(p.Key, new VxSchemaChecksum(p.Value));
195 // Read an array of checksums from a DBus message.
196 // Signature: a(sat)
197 public VxSchemaChecksums(WvDbusMsg reply)
199 var array = reply.iter().pop();
201 foreach (WvAutoCast i in array)
203 var ii = i.GetEnumerator();
204 string key = ii.pop();
205 var sums = ii.pop().Cast<UInt64>();
206 var cs = new VxSchemaChecksum(key, sums);
207 Add(cs.key, cs);
211 // Write the list of checksums to DBus in a(sat) format.
212 public void WriteChecksums(WvDbusWriter writer)
214 writer.WriteArray(8, this, (w2, p) => {
215 p.Value.Write(w2);
219 public void AddSum(string key, ulong checksum)
221 if (this.ContainsKey(key))
223 VxSchemaChecksum old = this[key];
225 List<ulong> list = new List<ulong>(old.checksums);
226 list.Add(checksum);
228 this[key] = new VxSchemaChecksum(key, list);
230 else
231 this.Add(key, new VxSchemaChecksum(key, checksum));
234 public static string GetDbusSignature()
236 return String.Format("a({0})", VxSchemaChecksum.GetDbusSignature());
239 public static void ParseKey(string key, out string type, out string name)
241 string[] parts = key.split("/", 2);
242 if (parts.Length != 2)
244 type = name = null;
245 return;
247 type = parts[0];
248 name = parts[1];
249 return;