/* __ *\ ** ________ ___ / / ___ Scala API ** ** / __/ __// _ | / / / _ | (c) 2003-2009, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** \* */ // $Id: BytePickle.scala 17856 2009-05-27 19:35:02Z dubochet $ package scala.io import scala.collection.mutable.{HashMap, ArrayBuffer} /** * Pickler combinators. * Based on a Haskell library by Andrew Kennedy, * see <a href="http://research.microsoft.com/~akenn/fun/" * target="_top">http://research.microsoft.com/~akenn/fun/</a>. * * @author Philipp Haller * @version 1.1 */ object BytePickle { abstract class SPU[T] { def appP(a: T, state: PicklerState): PicklerState def appU(state: UnPicklerState): (T, UnPicklerState) } def pickle[T](p: SPU[T], a: T): Array[Byte] = p.appP(a, new PicklerState(new Array[Byte](0), new PicklerEnv)).stream def unpickle[T](p: SPU[T], stream: Array[Byte]): T = p.appU(new UnPicklerState(stream, new UnPicklerEnv))._1 abstract class PU[T] { def appP(a: T, state: Array[Byte]): Array[Byte] def appU(state: Array[Byte]): (T, Array[Byte]) } def upickle[T](p: PU[T], a: T): Array[Byte] = p.appP(a, new Array[Byte](0)) def uunpickle[T](p: PU[T], stream: Array[Byte]): T = p.appU(stream)._1 class PicklerEnv extends HashMap[Any, Int] { private var cnt: Int = 64 def nextLoc() = { cnt += 1; cnt } } class UnPicklerEnv extends HashMap[Int, Any] { private var cnt: Int = 64 def nextLoc() = { cnt += 1; cnt } } class PicklerState(val stream: Array[Byte], val dict: PicklerEnv) class UnPicklerState(val stream: Array[Byte], val dict: UnPicklerEnv) abstract class RefDef case class Ref() extends RefDef case class Def() extends RefDef def refDef: PU[RefDef] = new PU[RefDef] { def appP(b: RefDef, s: Array[Byte]): Array[Byte] = b match { case Ref() => Array.concat(s, (List[Byte](0)).toArray) case Def() => Array.concat(s, (List[Byte](1)).toArray) }; def appU(s: Array[Byte]): (RefDef, Array[Byte]) = if (s(0) == 0) (Ref(), s.slice(1, s.length)) else (Def(), s.slice(1, s.length)); } val REF = 0 val DEF = 1 def unat: PU[Int] = new PU[Int] { def appP(n: Int, s: Array[Byte]): Array[Byte] = Array.concat(s, nat2Bytes(n)); def appU(s: Array[Byte]): (Int, Array[Byte]) = { var num = 0 def readNat: Int = { var b = 0; var x = 0; do { b = s(num) num += 1 x = (x << 7) + (b & 0x7f); } while ((b & 0x80) != 0); x } (readNat, s.slice(num, s.length)) } } def share[a](pa: SPU[a]): SPU[a] = new SPU[a] { def appP(v: a, state: PicklerState): PicklerState = { /* - is there some value equal to v associated with a location l in the pickle environment? - yes: write REF-tag to outstream together with l - no: write DEF-tag to outstream record current location l of outstream --> serialize value add entry to pickle environment, mapping v onto l */ val pe = state.dict pe.get(v) match { case None => val sPrime = refDef.appP(Def(), state.stream) val l = pe.nextLoc() val sPrimePrime = pa.appP(v, new PicklerState(sPrime, pe)) pe.update(v, l) return sPrimePrime case Some(l) => val sPrime = refDef.appP(Ref(), state.stream) return new PicklerState(unat.appP(l, sPrime), pe) } } def appU(state: UnPicklerState): (a, UnPicklerState) = { /* - first, read tag (i.e. DEF or REF) - if REF: read location l look up resulting value in unpickler environment - if DEF: record location l of input stream --> deserialize value v with argument deserializer add entry to unpickler environment, mapping l onto v */ val upe = state.dict val res = refDef.appU(state.stream) res._1 match { case Def() => val l = upe.nextLoc val res2 = pa.appU(new UnPicklerState(res._2, upe)) upe.update(l, res2._1) return res2 case Ref() => val res2 = unat.appU(res._2) // read location upe.get(res2._1) match { // lookup value in unpickler env case None => throw new IllegalArgumentException("invalid unpickler environment"); return null case Some(v) => return (v.asInstanceOf[a], new UnPicklerState(res2._2, upe)) } } } } def ulift[t](x: t): PU[t] = new PU[t] { def appP(a: t, state: Array[Byte]): Array[Byte] = if (x != a) { throw new IllegalArgumentException("value to be pickled (" + a + ") != " + x); state } else state; def appU(state: Array[Byte]) = (x, state) } def lift[t](x: t): SPU[t] = new SPU[t] { def appP(a: t, state: PicklerState): PicklerState = if (x != a) { /*throw new IllegalArgumentException("value to be pickled (" + a + ") != " + x);*/ state } else state; def appU(state: UnPicklerState) = (x, state) } def usequ[t,u](f: u => t, pa: PU[t], k: t => PU[u]): PU[u] = new PU[u] { def appP(b: u, s: Array[Byte]): Array[Byte] = { val a = f(b) val sPrime = pa.appP(a, s) val pb = k(a) val sPrimePrime = pb.appP(b, sPrime) sPrimePrime } def appU(s: Array[Byte]): (u, Array[Byte]) = { val resPa = pa.appU(s) val a = resPa._1 val sPrime = resPa._2 val pb = k(a) pb.appU(sPrime) } } def sequ[t,u](f: u => t, pa: SPU[t], k: t => SPU[u]): SPU[u] = new SPU[u] { def appP(b: u, s: PicklerState): PicklerState = { val a = f(b) val sPrime = pa.appP(a, s) val pb = k(a) pb.appP(b, sPrime) } def appU(s: UnPicklerState): (u, UnPicklerState) = { val resPa = pa.appU(s) val a = resPa._1 val sPrime = resPa._2 val pb = k(a) pb.appU(sPrime) } } def upair[a,b](pa: PU[a], pb: PU[b]): PU[(a,b)] = { def fst(p: (a,b)): a = p._1 def snd(p: (a,b)): b = p._2 usequ(fst, pa, (x: a) => usequ(snd, pb, (y: b) => ulift((x, y)))) } def pair[a,b](pa: SPU[a], pb: SPU[b]): SPU[(a,b)] = { def fst(p: (a,b)): a = p._1 def snd(p: (a,b)): b = p._2 sequ(fst, pa, (x: a) => sequ(snd, pb, (y: b) => lift((x, y)))) } def triple[a,b,c](pa: SPU[a], pb: SPU[b], pc: SPU[c]): SPU[(a,b,c)] = { def fst(p: (a,b,c)): a = p._1 def snd(p: (a,b,c)): b = p._2 def trd(p: (a,b,c)): c = p._3 sequ(fst, pa, (x: a) => sequ(snd, pb, (y: b) => sequ(trd, pc, (z: c) => lift((x, y, z))))) } def uwrap[a,b](i: a => b, j: b => a, pa: PU[a]): PU[b] = usequ(j, pa, (x: a) => ulift(i(x))) def wrap[a,b](i: a => b, j: b => a, pa: SPU[a]): SPU[b] = sequ(j, pa, (x: a) => lift(i(x))) def appendByte(a: Array[Byte], b: Int): Array[Byte] = Array.concat(a, (List[Byte](b.asInstanceOf[Byte])).toArray) def nat2Bytes(x: Int): Array[Byte] = { val buf = new ArrayBuffer[Byte] def writeNatPrefix(x: Int) { val y = x >>> 7; if (y != 0) writeNatPrefix(y); buf += ((x & 0x7f) | 0x80).asInstanceOf[Byte]; } val y = x >>> 7; if (y != 0) writeNatPrefix(y); buf += (x & 0x7f).asInstanceOf[Byte]; buf.toArray } def nat: SPU[Int] = new SPU[Int] { def appP(n: Int, s: PicklerState): PicklerState = { new PicklerState(Array.concat(s.stream, nat2Bytes(n)), s.dict); } def appU(s: UnPicklerState): (Int,UnPicklerState) = { var num = 0 def readNat: Int = { var b = 0 var x = 0 do { b = s.stream(num) num += 1 x = (x << 7) + (b & 0x7f); } while ((b & 0x80) != 0); x } (readNat, new UnPicklerState(s.stream.slice(num, s.stream.length), s.dict)) } } def byte: SPU[Byte] = new SPU[Byte] { def appP(b: Byte, s: PicklerState): PicklerState = new PicklerState(Array.concat(s.stream, (List[Byte](b)).toArray), s.dict); def appU(s: UnPicklerState): (Byte, UnPicklerState) = (s.stream(0), new UnPicklerState(s.stream.slice(1, s.stream.length), s.dict)); } def string: SPU[String] = share(wrap((a: Array[Byte]) => UTF8Codec.decode(a, 0, a.length), (s:String) => UTF8Codec.encode(s), bytearray)); def bytearray: SPU[Array[Byte]] = { wrap((l:List[Byte]) => l.toArray, (_.toList), list(byte)) } def bool: SPU[Boolean] = { def toEnum(b: Boolean) = if (b) 1 else 0 def fromEnum(n: Int) = if (n == 0) false else true wrap(fromEnum, toEnum, nat) } def ufixedList[A](pa: PU[A])(n: Int): PU[List[A]] = { def pairToList(p: (A, List[A])): List[A] = p._1 :: p._2; def listToPair(l: List[A]): (A, List[A]) = (l: @unchecked) match { case x :: xs => (x, xs) } if (n == 0) ulift(Nil) else uwrap(pairToList, listToPair, upair(pa, ufixedList(pa)(n-1))) } def fixedList[a](pa: SPU[a])(n: Int): SPU[List[a]] = { def pairToList(p: (a,List[a])): List[a] = p._1 :: p._2; def listToPair(l: List[a]): (a,List[a]) = (l: @unchecked) match { case x :: xs => (x, xs) } if (n == 0) lift(Nil) else wrap(pairToList, listToPair, pair(pa, fixedList(pa)(n-1))) } def list[a](pa: SPU[a]): SPU[List[a]] = sequ((l: List[a])=>l.length, nat, fixedList(pa)); def ulist[a](pa: PU[a]): PU[List[a]] = usequ((l:List[a]) => l.length, unat, ufixedList(pa)); def data[a](tag: a => Int, ps: List[()=>SPU[a]]): SPU[a] = sequ(tag, nat, (x: Int)=> ps.apply(x)()); }