1 /*
2  * Copyright 2017 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     https://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package trebuchet.io
18 
19 data class DataSlice(var buffer: ByteArray,
20                      var startIndex: Int,
21                      var endIndex: Int) {
22 
23     companion object {
24         val EmptyBuffer = ByteArray(0)
25     }
26 
27     constructor() : this(EmptyBuffer, 0, 0)
28     constructor(buffer: ByteArray) : this(buffer, 0, buffer.size)
29 
30     @Suppress("NOTHING_TO_INLINE")
getnull31     inline operator fun get(i: Int): Byte = buffer[startIndex + i]
32 
33     @Suppress("NOTHING_TO_INLINE")
34     inline fun slice(startIndex: Int, endIndex: Int = this.endIndex,
35                      dest: DataSlice = DataSlice()): DataSlice {
36         dest.set(buffer, this.startIndex + startIndex, this.startIndex + endIndex)
37         return dest
38     }
39 
40     inline val length: Int get() = endIndex - startIndex
41 
setnull42     fun set(buffer: ByteArray, startIndex: Int, endIndex: Int) {
43         this.buffer = buffer
44         this.startIndex = startIndex
45         this.endIndex = endIndex
46         _hasCachedHashCode = false
47     }
48 
equalsnull49     override fun equals(other: Any?): Boolean {
50         if (other === this) return true
51         if (other !is DataSlice) return false
52         if (length != other.length) return false
53         if (_hasCachedHashCode && other._hasCachedHashCode
54                 && hashCode() != other.hashCode()) return false
55         var myIndex = startIndex
56         var otherIndex = other.startIndex
57         while (myIndex < endIndex) {
58             if (buffer[myIndex] != other.buffer[otherIndex]) {
59                 return false
60             }
61             myIndex++
62             otherIndex++
63         }
64         return true
65     }
66 
67     private var _cachedHashCode: Int = 1
68     private var _hasCachedHashCode: Boolean = false
hashCodenull69     override fun hashCode(): Int {
70         if (_hasCachedHashCode) return _cachedHashCode
71         var hash = 1
72         for (i in startIndex..endIndex-1) {
73             hash = 31 * hash + buffer[i].toInt()
74         }
75         _cachedHashCode = hash
76         _hasCachedHashCode = true
77         return hash
78     }
79 
toStringnull80     override fun toString() = String(buffer, startIndex, length)
81 
82     fun compact(): DataSlice {
83         if (length - buffer.size < 50) {
84             return this
85         }
86         return DataSlice(buffer.copyOfRange(startIndex, endIndex))
87     }
88 }
89 
ByteArraynull90 fun ByteArray.asSlice(length: Int = this.size) = DataSlice(this, 0, length)
91 fun String.asSlice() = DataSlice(this.toByteArray())
92 
93 fun DataSlice.asText(): CharSequence {
94     val data = this
95     return object : CharSequence {
96         override val length: Int get() = data.length
97 
98         override fun get(index: Int): Char {
99             return data[index].toChar()
100         }
101 
102         override fun subSequence(startIndex: Int, endIndex: Int): CharSequence {
103             return data.slice(startIndex, endIndex).asText()
104         }
105     }
106 }