public void testRegex() { var rxlist = new Regex[] { new Regex(""), new Regex("foo", RegexOptions.IgnoreCase|RegexOptions.CultureInvariant), new Regex("x.*y", RegexOptions.Multiline), new Regex("a", RegexOptions.ExplicitCapture), new Regex("b", RegexOptions.Singleline), }; foreach(var rx in rxlist) { var recode = (Regex)BertCodec.Decode(BertCodec.Encode(rx)); AssertRegexEqual(recode, rx); } /* It would be nice to translate * IgnorePatternWhitespace to erlang's "extended", but * they differ in their handling of whitespace within * character classes. Maybe that's ok, given other * possible differences of interpretation? */ Assert.Throws<NotSupportedException>( () => { BertCodec.Encode(new Regex("", RegexOptions.IgnorePatternWhitespace)); } ); Assert.Throws<NotSupportedException>( () => { BertCodec.Encode(new Regex("", RegexOptions.RightToLeft)); } ); Assert.Throws<NotSupportedException>( () => { BertCodec.Encode(new Regex("", RegexOptions.ECMAScript)); } ); Assert.Throws<NotSupportedException>( () => { BertCodec.Encode(new Regex("", RegexOptions.IgnoreCase)); } ); Assert.Throws<NotSupportedException>( () => { BertCodec.Encode(new Regex("", RegexOptions.CultureInvariant)); } ); var decodeExamples = new List<Tuple<byte[], ArrayList, Regex>>() { Tuple.Create(new byte[0], new ArrayList(), new Regex("")), Tuple.Create(new byte[0], new ArrayList() { BertCodec.caseless }, new Regex("", RegexOptions.IgnoreCase|RegexOptions.CultureInvariant)), Tuple.Create(new byte[0], new ArrayList() { BertCodec.caseless, BertCodec.dotall }, new Regex("", RegexOptions.IgnoreCase|RegexOptions.CultureInvariant|RegexOptions.Singleline)), Tuple.Create(new byte[0], new ArrayList() { BertCodec.multiline }, new Regex("", RegexOptions.Multiline)), Tuple.Create(new byte[0], new ArrayList() { BertCodec.no_auto_capture }, new Regex("", RegexOptions.ExplicitCapture)), new Tuple<byte[], ArrayList, Regex>(new byte[0], new ArrayList() { new Atom("invalid") }, null), }; foreach(var t in decodeExamples) { var rx = new ETFTuple() { BertCodec.bert, BertCodec.regex, t.Item1, t.Item2 }; if(t.Item3 == null) { Assert.Throws<NotSupportedException>( () => BertCodec.bertDecode(rx)); } else { AssertRegexEqual(t.Item3, (Regex)BertCodec.bertDecode(rx)); } } }
internal static object bertDecode(object obj) { if(obj is ETFTuple) { var tuple = (ETFTuple)obj; if(tuple.Count > 1 && bert.Equals(tuple[0])) { switch(tuple.Count) { case 2: if(nil.Equals(tuple[1])) { obj = null; } else if(True.Equals(tuple[1])) { obj = true; } else if(False.Equals(tuple[1])) { obj = false; } else { goto default; } break; case 3: if(dict.Equals(tuple[1])) { var il = (IList)tuple[2]; var ht = new Hashtable(il.Count); foreach(object o in il) { ETFTuple item = (ETFTuple)o; var key = bertDecode(item[0]); byte[] b = key as byte[]; if(b != null) { lock(utf8) { key = utf8.GetString(b, 0, b.Length); } } ht.Add(key, bertDecode(item[1])); } obj = ht; break; } goto default; case 4: if(regex.Equals(tuple[1])) { var options = RegexOptions.None; foreach(var opt in ((IList)tuple[3]).Cast<Atom>()) { RegexOptions optflag; if(RegexOptionTranslations.TryGetValue(opt, out optflag)) { options |= optflag; } else { throw new NotSupportedException(string.Format("regex option: {0}", opt)); } } string pattern; lock(utf8) { pattern = utf8.GetString((byte[])tuple[2]); } obj = new Regex(pattern, options); break; } goto default; case 5: if(time.Equals(tuple[1])) { obj = BeginEpoch.Add(new TimeSpan( Convert.ToInt64(tuple[2]) * TicksPerMegaSecond + Convert.ToInt64(tuple[3]) * TimeSpan.TicksPerSecond + Convert.ToInt64(tuple[4]) * TimeSpan.TicksPerMillisecond )); break; } goto default; default: throw new NotSupportedException(string.Format("invalid bert type: {0}", tuple[1])); } } else { var nt = new ETFTuple(tuple.Count); foreach(var e in tuple) { nt.Add(bertDecode(e)); } obj = nt; } } else if(obj is byte[]) { // don't treat byte[] as IList } else if(obj is IList) { var il = (IList)obj; var nl = new ArrayList(il.Count); foreach(var e in il) { nl.Add(bertDecode(e)); } obj = nl; } return obj; }
internal static object DecodePart(byte[] encoded, ref int offset) { byte encoding_type = encoded[offset++]; switch(encoding_type) { case Constants.SMALL_INTEGER_EXT: return encoded[offset++]; case Constants.INTEGER_EXT: return DecodeInt(encoded, ref offset); case Constants.STRING_EXT: ushort slen = DecodeUshort(encoded, ref offset); var cl = new List<byte>(slen); for(var i = 0; i < slen; i++) { cl.Add(encoded[offset + i]); } offset += slen; return cl; case Constants.BINARY_EXT: int blen = (int)DecodeUint(encoded, ref offset); byte[] b = new byte[blen]; Buffer.BlockCopy(encoded, offset, b, 0, blen); offset += blen; return b; case Constants.LIST_EXT: uint llen = DecodeUint(encoded, ref offset); var list = new ArrayList((int)llen); DecodeList(encoded, ref offset, list, llen, true); return list; case Constants.SMALL_TUPLE_EXT: var stlen = encoded[offset++]; var st = new ETFTuple(stlen); DecodeList(encoded, ref offset, st, stlen); return st; case Constants.LARGE_TUPLE_EXT: uint ltlen = DecodeUint(encoded, ref offset); var lt = new ETFTuple((int)ltlen); DecodeList(encoded, ref offset, lt, ltlen); return lt; case Constants.SMALL_ATOM_EXT: var salen = encoded[offset++]; offset += salen; return new Atom(encoded, offset - salen, salen); case Constants.ATOM_EXT: var alen = DecodeUshort(encoded, ref offset); offset += alen; return new Atom(encoded, offset - alen, alen); case Constants.SMALL_BIG_EXT: return DecodeBig(encoded, ref offset, encoded[offset++]); case Constants.LARGE_BIG_EXT: int size = (int)DecodeUint(encoded, ref offset); return DecodeBig(encoded, ref offset, size); case Constants.NEW_FLOAT_EXT: return DecodeDouble(encoded, ref offset); case Constants.FLOAT_EXT: double dret; string ds = Encoding.ASCII.GetString(encoded, offset, Constants.FLOAT_EXT_BYTES); offset += Constants.FLOAT_EXT_BYTES; NumberStyles style = NumberStyles.AllowDecimalPoint | NumberStyles.AllowExponent | NumberStyles.AllowLeadingSign | NumberStyles.AllowLeadingWhite; if(Double.TryParse(ds, style, NumberFormatInfo.InvariantInfo, out dret)) { return dret; } else { throw new EncodingError(string.Format("invalid float encoding: \"{0}\"", ds)); } case Constants.NIL_EXT: return new ArrayList(); default: throw new EncodingError(string.Format("invalid encoding type: {0}", encoding_type)); } }
static object bertEncode(object obj) { if(obj == null) { obj = BertNil; } else if(obj is bool) { obj = (bool)obj ? BertTrue : BertFalse; } else if(obj is ETFTuple) { // has to come before IList case so tuples won't be converted var tuple = (ETFTuple)obj; var nt = new ETFTuple(tuple.Count); foreach(var e in tuple) { nt.Add(bertEncode(e)); } obj = nt; } else if(obj is byte[]) { // don't treat byte[] as IList } else if(obj is IList) { var il = (IList)obj; var nl = new ArrayList(il.Count); foreach(var e in il) { nl.Add(bertEncode(e)); } obj = nl; } else if(obj is IDictionary) { var id = (IDictionary)obj; var items = new ArrayList(id.Count); foreach(DictionaryEntry e in id) { items.Add(new ETFTuple() { bertEncode(e.Key), bertEncode(e.Value) }); } obj = new ETFTuple() { bert, dict, items }; } else if(obj is IDictionary<string, object>) { var id = (IDictionary<string, object> )obj; var items = new ArrayList(id.Count); foreach(var e in id) { items.Add(new ETFTuple() { bertEncode(e.Key), bertEncode(e.Value) }); } obj = new ETFTuple() { bert, dict, items }; } else if(obj is DateTime) { var dt = (DateTime)obj; TimeSpan span = dt - BeginEpoch; long megaSeconds = span.Ticks / TicksPerMegaSecond; long seconds = (span.Ticks % TicksPerMegaSecond) / TimeSpan.TicksPerSecond; long milliSeconds = (span.Ticks % TimeSpan.TicksPerSecond) / TimeSpan.TicksPerMillisecond; obj = new ETFTuple() { bert, time, megaSeconds, seconds, milliSeconds }; } else if(obj is Regex) { obj = encodeRegex((Regex)obj); } return obj; }