src/Pure/General/bytes.scala
changeset 75393 87ebf5a50283
parent 75382 81673c441ce3
child 75579 3362b6a5d697
equal deleted inserted replaced
75388:b3ca4a6ed74b 75393:87ebf5a50283
    13 import java.util.Base64
    13 import java.util.Base64
    14 
    14 
    15 import org.tukaani.xz.{XZInputStream, XZOutputStream}
    15 import org.tukaani.xz.{XZInputStream, XZOutputStream}
    16 
    16 
    17 
    17 
    18 object Bytes
    18 object Bytes {
    19 {
       
    20   val empty: Bytes = new Bytes(Array[Byte](), 0, 0)
    19   val empty: Bytes = new Bytes(Array[Byte](), 0, 0)
    21 
    20 
    22   def apply(s: CharSequence): Bytes =
    21   def apply(s: CharSequence): Bytes = {
    23   {
       
    24     val str = s.toString
    22     val str = s.toString
    25     if (str.isEmpty) empty
    23     if (str.isEmpty) empty
    26     else {
    24     else {
    27       val b = UTF8.bytes(str)
    25       val b = UTF8.bytes(str)
    28       new Bytes(b, 0, b.length)
    26       new Bytes(b, 0, b.length)
    42   val newline: Bytes = apply("\n")
    40   val newline: Bytes = apply("\n")
    43 
    41 
    44 
    42 
    45   /* base64 */
    43   /* base64 */
    46 
    44 
    47   def base64(s: String): Bytes =
    45   def base64(s: String): Bytes = {
    48   {
       
    49     val a = Base64.getDecoder.decode(s)
    46     val a = Base64.getDecoder.decode(s)
    50     new Bytes(a, 0, a.length)
    47     new Bytes(a, 0, a.length)
    51   }
    48   }
    52 
    49 
    53   object Decode_Base64 extends Scala.Fun("decode_base64")
    50   object Decode_Base64 extends Scala.Fun("decode_base64") {
    54   {
       
    55     val here = Scala_Project.here
    51     val here = Scala_Project.here
    56     def invoke(args: List[Bytes]): List[Bytes] =
    52     def invoke(args: List[Bytes]): List[Bytes] =
    57       List(base64(Library.the_single(args).text))
    53       List(base64(Library.the_single(args).text))
    58   }
    54   }
    59 
    55 
    60   object Encode_Base64 extends Scala.Fun("encode_base64")
    56   object Encode_Base64 extends Scala.Fun("encode_base64") {
    61   {
       
    62     val here = Scala_Project.here
    57     val here = Scala_Project.here
    63     def invoke(args: List[Bytes]): List[Bytes] =
    58     def invoke(args: List[Bytes]): List[Bytes] =
    64       List(Bytes(Library.the_single(args).base64))
    59       List(Bytes(Library.the_single(args).base64))
    65   }
    60   }
    66 
    61 
    83       }
    78       }
    84 
    79 
    85       new Bytes(out.toByteArray, 0, out.size)
    80       new Bytes(out.toByteArray, 0, out.size)
    86     }
    81     }
    87 
    82 
    88   def read(file: JFile): Bytes =
    83   def read(file: JFile): Bytes = {
    89   {
       
    90     val length = file.length
    84     val length = file.length
    91     val limit = if (length < 0 || length > Integer.MAX_VALUE) Integer.MAX_VALUE else length.toInt
    85     val limit = if (length < 0 || length > Integer.MAX_VALUE) Integer.MAX_VALUE else length.toInt
    92     using(new FileInputStream(file))(read_stream(_, limit = limit))
    86     using(new FileInputStream(file))(read_stream(_, limit = limit))
    93   }
    87   }
    94 
    88 
   106 }
   100 }
   107 
   101 
   108 final class Bytes private(
   102 final class Bytes private(
   109   protected val bytes: Array[Byte],
   103   protected val bytes: Array[Byte],
   110   protected val offset: Int,
   104   protected val offset: Int,
   111   val length: Int) extends CharSequence
   105   val length: Int) extends CharSequence {
   112 {
       
   113   /* equality */
   106   /* equality */
   114 
   107 
   115   override def equals(that: Any): Boolean =
   108   override def equals(that: Any): Boolean = {
   116   {
       
   117     that match {
   109     that match {
   118       case other: Bytes =>
   110       case other: Bytes =>
   119         if (this eq other) true
   111         if (this eq other) true
   120         else if (length != other.length) false
   112         else if (length != other.length) false
   121         else (0 until length).forall(i => bytes(offset + i) == other.bytes(other.offset + i))
   113         else (0 until length).forall(i => bytes(offset + i) == other.bytes(other.offset + i))
   122       case _ => false
   114       case _ => false
   123     }
   115     }
   124   }
   116   }
   125 
   117 
   126   private lazy val hash: Int =
   118   private lazy val hash: Int = {
   127   {
       
   128     var h = 0
   119     var h = 0
   129     for (i <- offset until offset + length) {
   120     for (i <- offset until offset + length) {
   130       val b = bytes(i).asInstanceOf[Int] & 0xFF
   121       val b = bytes(i).asInstanceOf[Int] & 0xFF
   131       h = 31 * h + b
   122       h = 31 * h + b
   132     }
   123     }
   144 
   135 
   145   def iterator: Iterator[Byte] =
   136   def iterator: Iterator[Byte] =
   146     for (i <- (offset until (offset + length)).iterator)
   137     for (i <- (offset until (offset + length)).iterator)
   147       yield bytes(i)
   138       yield bytes(i)
   148 
   139 
   149   def array: Array[Byte] =
   140   def array: Array[Byte] = {
   150   {
       
   151     val a = new Array[Byte](length)
   141     val a = new Array[Byte](length)
   152     System.arraycopy(bytes, offset, a, 0, length)
   142     System.arraycopy(bytes, offset, a, 0, length)
   153     a
   143     a
   154   }
   144   }
   155 
   145 
   156   def text: String = UTF8.decode_permissive(this)
   146   def text: String = UTF8.decode_permissive(this)
   157 
   147 
   158   def base64: String =
   148   def base64: String = {
   159   {
       
   160     val b =
   149     val b =
   161       if (offset == 0 && length == bytes.length) bytes
   150       if (offset == 0 && length == bytes.length) bytes
   162       else Bytes(bytes, offset, length).bytes
   151       else Bytes(bytes, offset, length).bytes
   163     Base64.getEncoder.encodeToString(b)
   152     Base64.getEncoder.encodeToString(b)
   164   }
   153   }
   165 
   154 
   166   def maybe_base64: (Boolean, String) =
   155   def maybe_base64: (Boolean, String) = {
   167   {
       
   168     val s = text
   156     val s = text
   169     if (this == Bytes(s)) (false, s) else (true, base64)
   157     if (this == Bytes(s)) (false, s) else (true, base64)
   170   }
   158   }
   171 
   159 
   172   override def toString: String = "Bytes(" + length + ")"
   160   override def toString: String = "Bytes(" + length + ")"
   189 
   177 
   190   def charAt(i: Int): Char =
   178   def charAt(i: Int): Char =
   191     if (0 <= i && i < length) (bytes(offset + i).asInstanceOf[Int] & 0xFF).asInstanceOf[Char]
   179     if (0 <= i && i < length) (bytes(offset + i).asInstanceOf[Int] & 0xFF).asInstanceOf[Char]
   192     else throw new IndexOutOfBoundsException
   180     else throw new IndexOutOfBoundsException
   193 
   181 
   194   def subSequence(i: Int, j: Int): Bytes =
   182   def subSequence(i: Int, j: Int): Bytes = {
   195   {
       
   196     if (0 <= i && i <= j && j <= length) new Bytes(bytes, offset + i, j - i)
   183     if (0 <= i && i <= j && j <= length) new Bytes(bytes, offset + i, j - i)
   197     else throw new IndexOutOfBoundsException
   184     else throw new IndexOutOfBoundsException
   198   }
   185   }
   199 
   186 
   200   def trim_line: Bytes =
   187   def trim_line: Bytes =
   215   /* XZ data compression */
   202   /* XZ data compression */
   216 
   203 
   217   def uncompress(cache: XZ.Cache = XZ.Cache()): Bytes =
   204   def uncompress(cache: XZ.Cache = XZ.Cache()): Bytes =
   218     using(new XZInputStream(stream(), cache))(Bytes.read_stream(_, hint = length))
   205     using(new XZInputStream(stream(), cache))(Bytes.read_stream(_, hint = length))
   219 
   206 
   220   def compress(options: XZ.Options = XZ.options(), cache: XZ.Cache = XZ.Cache()): Bytes =
   207   def compress(options: XZ.Options = XZ.options(), cache: XZ.Cache = XZ.Cache()): Bytes = {
   221   {
       
   222     val result = new ByteArrayOutputStream(length)
   208     val result = new ByteArrayOutputStream(length)
   223     using(new XZOutputStream(result, options, cache))(write_stream(_))
   209     using(new XZOutputStream(result, options, cache))(write_stream(_))
   224     new Bytes(result.toByteArray, 0, result.size)
   210     new Bytes(result.toByteArray, 0, result.size)
   225   }
   211   }
   226 
   212 
   227   def maybe_compress(options: XZ.Options = XZ.options(), cache: XZ.Cache = XZ.Cache())
   213   def maybe_compress(
   228     : (Boolean, Bytes) =
   214     options: XZ.Options = XZ.options(),
   229   {
   215     cache: XZ.Cache = XZ.Cache()
       
   216   ) : (Boolean, Bytes) = {
   230     val compressed = compress(options = options, cache = cache)
   217     val compressed = compress(options = options, cache = cache)
   231     if (compressed.length < length) (true, compressed) else (false, this)
   218     if (compressed.length < length) (true, compressed) else (false, this)
   232   }
   219   }
   233 }
   220 }