2 using System.Collections.Generic;
3 using System.Text.RegularExpressions;
6 namespace kinetica.Utils
13 internal sealed
class RecordKey
18 private static readonly Regex DATE_REGEX =
new Regex(
"\\A(\\d{4})-(\\d{2})-(\\d{2})$");
23 private static readonly Regex DATETIME_REGEX =
new Regex(
"\\A(?<year>\\d{4})-(?<month>\\d{2})-(?<day>\\d{2})(?<time>\\s+(?<hour>\\d{1,2}):(?<min>\\d{2}):(?<sec>\\d{2})(?:\\.(?<ms>\\d{1,6}))?)?$");
28 private static readonly Regex DECIMAL_REGEX =
new Regex(
"\\A\\s*(?<sign>[+-]?)((?<int>\\d+)(\\.(?<intfrac>\\d{0,4}))?|\\.(?<onlyfrac>\\d{1,4}))\\s*\\z");
33 private static readonly Regex IPV4_REGEX =
new Regex(
"\\A(?<a>\\d{1,3})\\.(?<b>\\d{1,3})\\.(?<c>\\d{1,3})\\.(?<d>\\d{1,3})$");
38 private static readonly Regex TIME_REGEX =
new Regex(
"\\A(?<hour>\\d{1,2}):(?<minute>\\d{2}):(?<seconds>\\d{2})(\\.(?<milliseconds>\\d{1,3}))?$");
43 private static readonly DateTime EPOCH_DATE =
new DateTime(1970, 1, 1);
48 private static readonly
int MIN_SUPPORTED_YEAR = 1000;
53 private static readonly
int MAX_SUPPORTED_YEAR = 2900;
58 private static readonly
int YEAR_1900 = 1900;
63 private static readonly TimeZoneInfo UTC = TimeZoneInfo.Utc;
65 private readonly byte[] buffer;
66 private readonly
int buffer_size;
67 private int current_size;
68 private int hash_code;
69 private bool is_valid;
70 private long routingHash;
77 public RecordKey(
int size)
81 +
"Size given: " + size);
84 buffer =
new byte[size];
101 public int hashCode()
103 return this.hash_code;
114 private bool isBufferFull(
bool throw_if_full =
true)
116 if (this.current_size == this.buffer_size)
136 private bool willBufferOverflow(
int n,
bool throw_if_overflow =
true)
139 if ((this.current_size + n) > this.buffer_size)
141 if (throw_if_overflow)
142 throw new KineticaException($
"The buffer (of size {buffer_size}) does not have sufficient room in it to put {n} more byte(s) (current size is {this.current_size}).");
155 private void add(byte b)
158 buffer.SetValue(b, current_size++);
167 public void addInt(
int? value)
170 this.willBufferOverflow(4);
184 byte[] int_bytes = BitConverter.GetBytes((int)value);
187 foreach (byte b
in int_bytes)
196 public void addInt8(
int? value)
199 this.willBufferOverflow(1);
210 this.add((byte)value);
218 public void addInt16(
int? value)
221 this.willBufferOverflow(2);
233 byte[] short_bytes = BitConverter.GetBytes((short)value);
236 foreach (byte b
in short_bytes)
246 public void addLong(
long? value)
249 this.willBufferOverflow(8);
267 byte[] long_bytes = BitConverter.GetBytes((long)value);
270 foreach (byte b
in long_bytes)
279 public void addFloat(
float? value)
282 this.willBufferOverflow(4);
288 this.add((byte)0.0f);
289 this.add((byte)0.0f);
290 this.add((byte)0.0f);
291 this.add((byte)0.0f);
296 byte[] float_bytes = BitConverter.GetBytes((float)value);
299 foreach (byte b
in float_bytes)
309 public void addDouble(
double? value)
312 this.willBufferOverflow(8);
330 byte[] double_bytes = BitConverter.GetBytes((double)value);
333 foreach (byte b
in double_bytes)
344 public void addString(
string value)
355 System.Text.Encoding encoding =
new System.Text.UTF8Encoding();
356 byte[] input = encoding.GetBytes(value);
357 MurMurHash3.murmurhash3_x64_128(input, 0, (uint)input.Length, 10, out murmur);
360 this.addLong(murmur.val1);
373 public void addCharN(
string value,
int N)
376 this.willBufferOverflow(N);
384 for (
int i = 0; i < N; ++i)
392 byte[] bytes = System.Text.Encoding.UTF8.GetBytes(value);
393 int byte_count = bytes.GetLength(0);
403 for (
int i = N; i > byte_count; --i)
409 for (
int i = (byte_count - 1); i >= 0; --i)
422 public void addDate(
string value)
425 this.isBufferFull(
true);
435 Match match = DATE_REGEX.Match(value);
439 this.is_valid =
false;
445 int year, month, day;
447 System.Globalization.GregorianCalendar calendar =
new System.Globalization.GregorianCalendar();
452 year = int.Parse(match.Groups[1].ToString());
453 month = int.Parse(match.Groups[2].ToString());
454 day = int.Parse(match.Groups[3].ToString());
455 date =
new DateTime(year, month, day, calendar);
461 this.is_valid =
false;
466 if ((year < MIN_SUPPORTED_YEAR) || (year > MAX_SUPPORTED_YEAR))
469 this.is_valid =
false;
473 int fixed_day_of_week = ((int)calendar.GetDayOfWeek(date) + 1);
476 int date_integer = (((year - YEAR_1900) << 21)
479 | (calendar.GetDayOfYear(date) << 3)
480 | fixed_day_of_week);
481 this.addInt(date_integer);
491 public void addDateTime(
string value)
494 this.isBufferFull(
true);
504 Match match = DATETIME_REGEX.Match(value);
508 this.is_valid =
false;
515 int year, month, day;
521 System.Globalization.GregorianCalendar calendar =
new System.Globalization.GregorianCalendar();
526 year = int.Parse(match.Groups[
"year"].Value);
527 month = int.Parse(match.Groups[
"month"].Value);
528 day = int.Parse(match.Groups[
"day"].Value);
531 Group time_group = match.Groups[
"time"];
532 if (time_group.Success)
534 hour = int.Parse(match.Groups[
"hour"].Value);
535 minute = int.Parse(match.Groups[
"min"].Value);
536 second = int.Parse(match.Groups[
"sec"].Value);
539 Group ms_group = match.Groups[
"ms"];
540 if (ms_group.Success)
542 msecond = int.Parse(match.Groups[
"ms"].Value);
544 switch (ms_group.Value.Length)
547 msecond *= 100;
break;
549 msecond *= 10;
break;
552 msecond /= 10;
break;
554 msecond /= 100;
break;
556 msecond /= 1000;
break;
562 date =
new DateTime(year, month, day, hour, minute, second, msecond, calendar);
568 this.is_valid =
false;
573 if ((year < MIN_SUPPORTED_YEAR) || (year > MAX_SUPPORTED_YEAR))
576 this.is_valid =
false;
580 int fixed_day_of_week = ((int)calendar.GetDayOfWeek(date) + 1);
583 long datetime_long = (long)((((
long)(year - YEAR_1900)) << 53)
584 | (((
long)month) << 49)
585 | (((
long)day) << 44)
586 | (((
long)hour) << 39)
587 | (((
long)minute) << 33)
588 | (((
long)second) << 27)
589 | (((
long)msecond) << 17)
590 | (((
long)calendar.GetDayOfYear(date)) << 8)
591 | (((long)fixed_day_of_week) << 5));
592 this.addLong(datetime_long);
602 public void addDecimal(
string value)
605 this.isBufferFull(
true);
615 Match match = DECIMAL_REGEX.Match(value);
619 this.is_valid =
false;
629 Group integral_group = match.Groups[
"int"];
630 Group fraction_with_integral_group = match.Groups[
"intfrac"];
631 Group frac_only_group = match.Groups[
"onlyfrac"];
633 if (integral_group.Success)
635 decimal_value = long.Parse(integral_group.Value);
637 if (fraction_with_integral_group.Success)
641 if (fraction_with_integral_group.Value.Length > 0)
642 fraction = long.Parse(fraction_with_integral_group.Value);
646 long integral_part = decimal_value * (long)Math.Pow(10, fraction_with_integral_group.Value.Length);
647 decimal_value = integral_part + fraction;
650 switch (fraction_with_integral_group.Value.Length)
653 decimal_value *= 1000;
break;
655 decimal_value *= 100;
break;
657 decimal_value *= 10;
break;
661 else if (frac_only_group.Success)
663 decimal_value = long.Parse(frac_only_group.Value);
666 switch (frac_only_group.Value.Length)
669 decimal_value *= 1000;
break;
671 decimal_value *= 100;
break;
673 decimal_value *= 10;
break;
680 Group sign_group = match.Groups[
"sign"];
681 if (sign_group.Success)
683 if (sign_group.Value ==
"-")
684 decimal_value = (-1) * decimal_value;
691 this.is_valid =
false;
696 this.addLong(decimal_value);
706 public void addIPv4(
string value)
709 this.isBufferFull(
true);
719 Match match = IPV4_REGEX.Match(value);
723 this.is_valid =
false;
734 a = int.Parse(match.Groups[
"a"].Value);
735 b = int.Parse(match.Groups[
"b"].Value);
736 c = int.Parse(match.Groups[
"c"].Value);
737 d = int.Parse(match.Groups[
"d"].Value);
743 this.is_valid =
false;
749 if ((a > 255) || (b > 255) || (c > 255) || (d > 255))
752 this.is_valid =
false;
757 int ipv4_integer = ((a << 24) | (b << 16) | (c << 8) | d);
758 this.addInt(ipv4_integer);
769 public void addTime(
string value)
772 this.isBufferFull(
true);
782 Match match = TIME_REGEX.Match(value);
786 this.is_valid =
false;
792 uint hour, minute, second, milliseconds;
797 hour = uint.Parse(match.Groups[
"hour"].Value);
798 minute = uint.Parse(match.Groups[
"minute"].Value);
799 second = uint.Parse(match.Groups[
"seconds"].Value);
800 Group msec_group = match.Groups[
"milliseconds"];
804 if (msec_group.Success)
806 milliseconds = uint.Parse(msec_group.Value);
809 switch (msec_group.Value.Length)
812 milliseconds *= 100;
break;
814 milliseconds *= 10;
break;
822 this.is_valid =
false;
827 if ((hour > 23) || (minute > 59) || (second > 59))
830 this.is_valid =
false;
835 int time_integer = (int)((hour << 26) | (minute << 20) | (second << 14) | (milliseconds << 4));
836 this.addInt(time_integer);
844 public void addTimeStamp(
long? value)
854 DateTime time = EPOCH_DATE.AddMilliseconds((double)value);
855 long fixed_day_of_week = ((long)time.DayOfWeek + 1);
857 long timestamp = (long)((((
long)(time.Year - YEAR_1900)) << 53)
858 | (((
long)(time.Month)) << 49)
859 | (((
long)time.Day) << 44)
860 | (((long)time.Hour) << 39)
861 | (((
long)time.Minute) << 33)
862 | (((long)time.Second) << 27)
863 | (((
long)time.Millisecond) << 17)
864 | (((long)time.DayOfYear) << 8)
865 | (fixed_day_of_week << 5));
866 this.addLong(timestamp);
877 public void computHashes()
880 if (this.current_size != this.buffer_size)
881 throw new KineticaException(
"The RecordKey buffer is not full; check that all the relevant values have been added.");
885 MurMurHash3.murmurhash3_x64_128(this.buffer, 0, (uint)this.buffer_size, 10, out murmur);
888 this.routingHash = murmur.val1;
889 this.hash_code = (int)(this.routingHash ^ ((this.routingHash >> 32) & 0x0000ffffL));
900 public int route(IList<int> routingTable)
905 return (routingTable[Math.Abs((
int)(
this.routingHash % routingTable.Count))] - 1);