1 /***********************************************************************
2  * Copyright (c) 2013-2025 General Atomics Integrated Intelligence, Inc.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Apache License, Version 2.0
5  * which accompanies this distribution and is available at
6  * https://www.apache.org/licenses/LICENSE-2.0
7  ***********************************************************************/
8 
9 package org.locationtech.geomesa.utils.collection
10 
11 /**
12   * Growable byte queue that holds values in a circular array to minimize allocations
13   *
14   * @param initialSize initial buffer size
15   */
16 class CircularByteQueue(initialSize: Int = 16) {
17 
18   private var array = Array.ofDim[Byte](initialSize)
19   private var start = 0
20   private var length = 0
21 
22   /**
23     * Number of bytes stored in this buffer
24     *
25     * @return
26     */
27   def size: Int = length
28 
29   /**
30     * Current remaining capacity. In general this is not relevant, as the queue will grow as needed,
31     * but it can be useful for debugging
32     *
33     * @return current capacity of the buffer
34     */
35   def capacity: Int = array.length - length
36 
37   /**
38     * Enqueue a single byte
39     *
40     * @param byte byte
41     */
42   def += (byte: Byte): Unit = enqueue(byte)
43 
44   /**
45     * Enqueue multiple bytes
46     *
47     * @param bytes bytes
48     */
49   def ++= (bytes: Array[Byte]): Unit = enqueue(bytes, 0, bytes.length)
50 
51   /**
52     * Enqueue a single byte
53     *
54     * @param byte byte
55     */
56   def enqueue(byte: Byte): Unit = {
57     ensure(1)
58     var last = start + length
59     if (last >= array.length) {
60       last -= array.length
61     }
62     array(last) = byte
63     length += 1
64   }
65 
66   /**
67     * Enqueue multiple bytes
68     *
69     * @param bytes bytes
70     * @param offset offset into the array to start reading, must be valid for the bytes passed in
71     * @param count number of bytes to enqueue, must be valid for the size of the bytes and offset passed in
72     */
73   def enqueue(bytes: Array[Byte], offset: Int, count: Int): Unit = {
74     ensure(count)
75     var first = start + length
76     if (first >= array.length) {
77       first -= array.length
78     }
79     if (first + count < array.length) {
80       System.arraycopy(bytes, offset, array, first, count)
81     } else {
82       val tail = array.length - first
83       System.arraycopy(bytes, offset, array, first, tail)
84       System.arraycopy(bytes, offset + tail, array, 0, count - tail)
85     }
86     length += count
87   }
88 
89   /**
90     * Dequeue bytes
91     *
92     * @param count number of bytes to remove
93     * @return
94     */
95   def dequeue(count: Int): Array[Byte] = {
96     val total = math.min(length, count)
97     val result = Array.ofDim[Byte](total)
98     if (start + total <= array.length) {
99       System.arraycopy(array, start, result, 0, total)
100     } else {
101       val tail = array.length - start
102       System.arraycopy(array, start, result, 0, tail)
103       System.arraycopy(array, 0, result, tail, total - tail)
104     }
105     start += total
106     if (start >= array.length) {
107       start -= array.length
108     }
109     length -= total
110     result
111   }
112 
113   /**
114     * Drop bytes
115     *
116     * @param count number of bytes to drop
117     */
118   def drop(count: Int): Unit = {
119     val total = math.min(length, count)
120     start += total
121     if (start >= array.length) {
122       start -= array.length
123     }
124     length -= total
125   }
126 
127   /**
128     * Grow the array as needed to fit an additional number of bytes
129     *
130     * @param count bytes to fit
131     */
132   private def ensure(count: Int): Unit = {
133     if (length + count > array.length) {
134       var newSize = array.length * 2
135       while (newSize < length + count) {
136         newSize = newSize * 2
137       }
138       val tmp = Array.ofDim[Byte](newSize)
139       if (start + length <= array.length) {
140         System.arraycopy(array, start, tmp, 0, length)
141       } else {
142         val tailBytes = array.length - start
143         System.arraycopy(array, start, tmp, 0, tailBytes)
144         System.arraycopy(array, 0, tmp, tailBytes, length - tailBytes)
145       }
146       array = tmp
147       start = 0
148     }
149   }
150 }
Line Stmt Id Pos Tree Symbol Tests Code
18 2678 751 - 762 Select org.locationtech.geomesa.utils.collection.CircularByteQueue.initialSize CircularByteQueue.this.initialSize
18 2679 733 - 763 ApplyToImplicitArgs scala.Array.ofDim scala.Array.ofDim[Byte](CircularByteQueue.this.initialSize)((ClassTag.Byte: scala.reflect.ClassTag[Byte]))
19 2680 786 - 787 Literal <nosymbol> 0
20 2681 811 - 812 Literal <nosymbol> 0
27 2682 909 - 915 Select org.locationtech.geomesa.utils.collection.CircularByteQueue.length CircularByteQueue.this.length
35 2683 1160 - 1166 Select org.locationtech.geomesa.utils.collection.CircularByteQueue.length CircularByteQueue.this.length
35 2684 1145 - 1166 Apply scala.Int.- CircularByteQueue.this.array.length.-(CircularByteQueue.this.length)
42 2685 1268 - 1281 Apply org.locationtech.geomesa.utils.collection.CircularByteQueue.enqueue CircularByteQueue.this.enqueue(byte)
49 2686 1410 - 1411 Literal <nosymbol> 0
49 2687 1413 - 1425 Select scala.Array.length bytes.length
49 2688 1395 - 1426 Apply org.locationtech.geomesa.utils.collection.CircularByteQueue.enqueue CircularByteQueue.this.enqueue(bytes, 0, bytes.length)
57 2689 1538 - 1547 Apply org.locationtech.geomesa.utils.collection.CircularByteQueue.ensure CircularByteQueue.this.ensure(1)
58 2690 1571 - 1577 Select org.locationtech.geomesa.utils.collection.CircularByteQueue.length CircularByteQueue.this.length
58 2691 1563 - 1577 Apply scala.Int.+ CircularByteQueue.this.start.+(CircularByteQueue.this.length)
59 2692 1594 - 1606 Select scala.Array.length CircularByteQueue.this.array.length
59 2693 1586 - 1606 Apply scala.Int.>= last.>=(CircularByteQueue.this.array.length)
59 2697 1582 - 1582 Literal <nosymbol> ()
59 2698 1582 - 1582 Block <nosymbol> ()
60 2694 1624 - 1636 Select scala.Array.length CircularByteQueue.this.array.length
60 2695 1616 - 1636 Apply scala.Int.- last.-(CircularByteQueue.this.array.length)
60 2696 1616 - 1636 Assign <nosymbol> last = last.-(CircularByteQueue.this.array.length)
62 2699 1647 - 1665 Apply scala.Array.update CircularByteQueue.this.array.update(last, byte)
63 2700 1670 - 1681 Apply scala.Int.+ CircularByteQueue.this.length.+(1)
63 2701 1670 - 1681 Apply org.locationtech.geomesa.utils.collection.CircularByteQueue.length_= CircularByteQueue.this.length_=(CircularByteQueue.this.length.+(1))
74 2702 2039 - 2052 Apply org.locationtech.geomesa.utils.collection.CircularByteQueue.ensure CircularByteQueue.this.ensure(count)
75 2703 2077 - 2083 Select org.locationtech.geomesa.utils.collection.CircularByteQueue.length CircularByteQueue.this.length
75 2704 2069 - 2083 Apply scala.Int.+ CircularByteQueue.this.start.+(CircularByteQueue.this.length)
76 2705 2101 - 2113 Select scala.Array.length CircularByteQueue.this.array.length
76 2706 2092 - 2113 Apply scala.Int.>= first.>=(CircularByteQueue.this.array.length)
76 2710 2088 - 2088 Literal <nosymbol> ()
76 2711 2088 - 2088 Block <nosymbol> ()
77 2707 2132 - 2144 Select scala.Array.length CircularByteQueue.this.array.length
77 2708 2123 - 2144 Apply scala.Int.- first.-(CircularByteQueue.this.array.length)
77 2709 2123 - 2144 Assign <nosymbol> first = first.-(CircularByteQueue.this.array.length)
79 2712 2175 - 2187 Select scala.Array.length CircularByteQueue.this.array.length
79 2713 2159 - 2187 Apply scala.Int.< first.+(count).<(CircularByteQueue.this.array.length)
80 2714 2229 - 2234 Select org.locationtech.geomesa.utils.collection.CircularByteQueue.array CircularByteQueue.this.array
80 2715 2197 - 2249 Apply java.lang.System.arraycopy java.lang.System.arraycopy(bytes, offset, CircularByteQueue.this.array, first, count)
80 2716 2197 - 2249 Block java.lang.System.arraycopy java.lang.System.arraycopy(bytes, offset, CircularByteQueue.this.array, first, count)
81 2725 2261 - 2433 Block <nosymbol> { val tail: Int = CircularByteQueue.this.array.length.-(first); java.lang.System.arraycopy(bytes, offset, CircularByteQueue.this.array, first, tail); java.lang.System.arraycopy(bytes, offset.+(tail), CircularByteQueue.this.array, 0, count.-(tail)) }
82 2717 2280 - 2300 Apply scala.Int.- CircularByteQueue.this.array.length.-(first)
83 2718 2339 - 2344 Select org.locationtech.geomesa.utils.collection.CircularByteQueue.array CircularByteQueue.this.array
83 2719 2307 - 2358 Apply java.lang.System.arraycopy java.lang.System.arraycopy(bytes, offset, CircularByteQueue.this.array, first, tail)
84 2720 2389 - 2402 Apply scala.Int.+ offset.+(tail)
84 2721 2404 - 2409 Select org.locationtech.geomesa.utils.collection.CircularByteQueue.array CircularByteQueue.this.array
84 2722 2411 - 2412 Literal <nosymbol> 0
84 2723 2414 - 2426 Apply scala.Int.- count.-(tail)
84 2724 2365 - 2427 Apply java.lang.System.arraycopy java.lang.System.arraycopy(bytes, offset.+(tail), CircularByteQueue.this.array, 0, count.-(tail))
86 2726 2438 - 2453 Apply scala.Int.+ CircularByteQueue.this.length.+(count)
86 2727 2438 - 2453 Apply org.locationtech.geomesa.utils.collection.CircularByteQueue.length_= CircularByteQueue.this.length_=(CircularByteQueue.this.length.+(count))
96 2728 2625 - 2631 Select org.locationtech.geomesa.utils.collection.CircularByteQueue.length CircularByteQueue.this.length
96 2729 2616 - 2639 Apply scala.math.min scala.math.`package`.min(CircularByteQueue.this.length, count)
97 2730 2657 - 2681 ApplyToImplicitArgs scala.Array.ofDim scala.Array.ofDim[Byte](total)((ClassTag.Byte: scala.reflect.ClassTag[Byte]))
98 2731 2707 - 2719 Select scala.Array.length CircularByteQueue.this.array.length
98 2732 2690 - 2719 Apply scala.Int.<= CircularByteQueue.this.start.+(total).<=(CircularByteQueue.this.array.length)
99 2733 2746 - 2751 Select org.locationtech.geomesa.utils.collection.CircularByteQueue.array CircularByteQueue.this.array
99 2734 2753 - 2758 Select org.locationtech.geomesa.utils.collection.CircularByteQueue.start CircularByteQueue.this.start
99 2735 2768 - 2769 Literal <nosymbol> 0
99 2736 2729 - 2777 Apply java.lang.System.arraycopy java.lang.System.arraycopy(CircularByteQueue.this.array, CircularByteQueue.this.start, result, 0, total)
99 2737 2729 - 2777 Block java.lang.System.arraycopy java.lang.System.arraycopy(CircularByteQueue.this.array, CircularByteQueue.this.start, result, 0, total)
100 2748 2789 - 2949 Block <nosymbol> { val tail: Int = CircularByteQueue.this.array.length.-(CircularByteQueue.this.start); java.lang.System.arraycopy(CircularByteQueue.this.array, CircularByteQueue.this.start, result, 0, tail); java.lang.System.arraycopy(CircularByteQueue.this.array, 0, result, tail, total.-(tail)) }
101 2738 2823 - 2828 Select org.locationtech.geomesa.utils.collection.CircularByteQueue.start CircularByteQueue.this.start
101 2739 2808 - 2828 Apply scala.Int.- CircularByteQueue.this.array.length.-(CircularByteQueue.this.start)
102 2740 2852 - 2857 Select org.locationtech.geomesa.utils.collection.CircularByteQueue.array CircularByteQueue.this.array
102 2741 2859 - 2864 Select org.locationtech.geomesa.utils.collection.CircularByteQueue.start CircularByteQueue.this.start
102 2742 2874 - 2875 Literal <nosymbol> 0
102 2743 2835 - 2882 Apply java.lang.System.arraycopy java.lang.System.arraycopy(CircularByteQueue.this.array, CircularByteQueue.this.start, result, 0, tail)
103 2744 2906 - 2911 Select org.locationtech.geomesa.utils.collection.CircularByteQueue.array CircularByteQueue.this.array
103 2745 2913 - 2914 Literal <nosymbol> 0
103 2746 2930 - 2942 Apply scala.Int.- total.-(tail)
103 2747 2889 - 2943 Apply java.lang.System.arraycopy java.lang.System.arraycopy(CircularByteQueue.this.array, 0, result, tail, total.-(tail))
105 2749 2954 - 2968 Apply scala.Int.+ CircularByteQueue.this.start.+(total)
105 2750 2954 - 2968 Apply org.locationtech.geomesa.utils.collection.CircularByteQueue.start_= CircularByteQueue.this.start_=(CircularByteQueue.this.start.+(total))
106 2751 2986 - 2998 Select scala.Array.length CircularByteQueue.this.array.length
106 2752 2977 - 2998 Apply scala.Int.>= CircularByteQueue.this.start.>=(CircularByteQueue.this.array.length)
106 2757 2973 - 2973 Literal <nosymbol> ()
106 2758 2973 - 2973 Block <nosymbol> ()
107 2753 3017 - 3029 Select scala.Array.length CircularByteQueue.this.array.length
107 2754 3008 - 3029 Apply scala.Int.- CircularByteQueue.this.start.-(CircularByteQueue.this.array.length)
107 2755 3008 - 3029 Apply org.locationtech.geomesa.utils.collection.CircularByteQueue.start_= CircularByteQueue.this.start_=(CircularByteQueue.this.start.-(CircularByteQueue.this.array.length))
107 2756 3008 - 3029 Block org.locationtech.geomesa.utils.collection.CircularByteQueue.start_= CircularByteQueue.this.start_=(CircularByteQueue.this.start.-(CircularByteQueue.this.array.length))
109 2759 3040 - 3055 Apply scala.Int.- CircularByteQueue.this.length.-(total)
109 2760 3040 - 3055 Apply org.locationtech.geomesa.utils.collection.CircularByteQueue.length_= CircularByteQueue.this.length_=(CircularByteQueue.this.length.-(total))
119 2761 3209 - 3215 Select org.locationtech.geomesa.utils.collection.CircularByteQueue.length CircularByteQueue.this.length
119 2762 3200 - 3223 Apply scala.math.min scala.math.`package`.min(CircularByteQueue.this.length, count)
120 2763 3228 - 3242 Apply scala.Int.+ CircularByteQueue.this.start.+(total)
120 2764 3228 - 3242 Apply org.locationtech.geomesa.utils.collection.CircularByteQueue.start_= CircularByteQueue.this.start_=(CircularByteQueue.this.start.+(total))
121 2765 3260 - 3272 Select scala.Array.length CircularByteQueue.this.array.length
121 2766 3251 - 3272 Apply scala.Int.>= CircularByteQueue.this.start.>=(CircularByteQueue.this.array.length)
121 2771 3247 - 3247 Literal <nosymbol> ()
121 2772 3247 - 3247 Block <nosymbol> ()
122 2767 3291 - 3303 Select scala.Array.length CircularByteQueue.this.array.length
122 2768 3282 - 3303 Apply scala.Int.- CircularByteQueue.this.start.-(CircularByteQueue.this.array.length)
122 2769 3282 - 3303 Apply org.locationtech.geomesa.utils.collection.CircularByteQueue.start_= CircularByteQueue.this.start_=(CircularByteQueue.this.start.-(CircularByteQueue.this.array.length))
122 2770 3282 - 3303 Block org.locationtech.geomesa.utils.collection.CircularByteQueue.start_= CircularByteQueue.this.start_=(CircularByteQueue.this.start.-(CircularByteQueue.this.array.length))
124 2773 3314 - 3329 Apply scala.Int.- CircularByteQueue.this.length.-(total)
124 2774 3314 - 3329 Apply org.locationtech.geomesa.utils.collection.CircularByteQueue.length_= CircularByteQueue.this.length_=(CircularByteQueue.this.length.-(total))
133 2775 3522 - 3534 Select scala.Array.length CircularByteQueue.this.array.length
133 2776 3505 - 3534 Apply scala.Int.> CircularByteQueue.this.length.+(count).>(CircularByteQueue.this.array.length)
133 2808 3536 - 4032 Block <nosymbol> { var newSize: Int = CircularByteQueue.this.array.length.*(2); while$1(){ if (newSize.<(CircularByteQueue.this.length.+(count))) { newSize = newSize.*(2); while$1() } else () }; val tmp: Array[Byte] = scala.Array.ofDim[Byte](newSize)((ClassTag.Byte: scala.reflect.ClassTag[Byte])); if (CircularByteQueue.this.start.+(CircularByteQueue.this.length).<=(CircularByteQueue.this.array.length)) java.lang.System.arraycopy(CircularByteQueue.this.array, CircularByteQueue.this.start, tmp, 0, CircularByteQueue.this.length) else { val tailBytes: Int = CircularByteQueue.this.array.length.-(CircularByteQueue.this.start); java.lang.System.arraycopy(CircularByteQueue.this.array, CircularByteQueue.this.start, tmp, 0, tailBytes); java.lang.System.arraycopy(CircularByteQueue.this.array, 0, tmp, tailBytes, CircularByteQueue.this.length.-(tailBytes)) }; CircularByteQueue.this.array_=(tmp); CircularByteQueue.this.start_=(0) }
133 2809 3501 - 3501 Literal <nosymbol> ()
133 2810 3501 - 3501 Block <nosymbol> ()
134 2777 3558 - 3574 Apply scala.Int.* CircularByteQueue.this.array.length.*(2)
135 2778 3598 - 3612 Apply scala.Int.+ CircularByteQueue.this.length.+(count)
135 2779 3588 - 3612 Apply scala.Int.< newSize.<(CircularByteQueue.this.length.+(count))
135 2782 3624 - 3645 Block <nosymbol> { newSize = newSize.*(2); while$1() }
135 2783 3581 - 3581 Literal <nosymbol> ()
135 2784 3581 - 3581 Block <nosymbol> ()
136 2780 3634 - 3645 Apply scala.Int.* newSize.*(2)
136 2781 3632 - 3632 Apply org.locationtech.geomesa.utils.collection.CircularByteQueue.while$1 while$1()
138 2785 3670 - 3696 ApplyToImplicitArgs scala.Array.ofDim scala.Array.ofDim[Byte](newSize)((ClassTag.Byte: scala.reflect.ClassTag[Byte]))
139 2786 3715 - 3721 Select org.locationtech.geomesa.utils.collection.CircularByteQueue.length CircularByteQueue.this.length
139 2787 3725 - 3737 Select scala.Array.length CircularByteQueue.this.array.length
139 2788 3707 - 3737 Apply scala.Int.<= CircularByteQueue.this.start.+(CircularByteQueue.this.length).<=(CircularByteQueue.this.array.length)
140 2789 3766 - 3771 Select org.locationtech.geomesa.utils.collection.CircularByteQueue.array CircularByteQueue.this.array
140 2790 3773 - 3778 Select org.locationtech.geomesa.utils.collection.CircularByteQueue.start CircularByteQueue.this.start
140 2791 3785 - 3786 Literal <nosymbol> 0
140 2792 3788 - 3794 Select org.locationtech.geomesa.utils.collection.CircularByteQueue.length CircularByteQueue.this.length
140 2793 3749 - 3795 Apply java.lang.System.arraycopy java.lang.System.arraycopy(CircularByteQueue.this.array, CircularByteQueue.this.start, tmp, 0, CircularByteQueue.this.length)
140 2794 3749 - 3795 Block java.lang.System.arraycopy java.lang.System.arraycopy(CircularByteQueue.this.array, CircularByteQueue.this.start, tmp, 0, CircularByteQueue.this.length)
141 2805 3809 - 3992 Block <nosymbol> { val tailBytes: Int = CircularByteQueue.this.array.length.-(CircularByteQueue.this.start); java.lang.System.arraycopy(CircularByteQueue.this.array, CircularByteQueue.this.start, tmp, 0, tailBytes); java.lang.System.arraycopy(CircularByteQueue.this.array, 0, tmp, tailBytes, CircularByteQueue.this.length.-(tailBytes)) }
142 2795 3850 - 3855 Select org.locationtech.geomesa.utils.collection.CircularByteQueue.start CircularByteQueue.this.start
142 2796 3835 - 3855 Apply scala.Int.- CircularByteQueue.this.array.length.-(CircularByteQueue.this.start)
143 2797 3881 - 3886 Select org.locationtech.geomesa.utils.collection.CircularByteQueue.array CircularByteQueue.this.array
143 2798 3888 - 3893 Select org.locationtech.geomesa.utils.collection.CircularByteQueue.start CircularByteQueue.this.start
143 2799 3900 - 3901 Literal <nosymbol> 0
143 2800 3864 - 3913 Apply java.lang.System.arraycopy java.lang.System.arraycopy(CircularByteQueue.this.array, CircularByteQueue.this.start, tmp, 0, tailBytes)
144 2801 3939 - 3944 Select org.locationtech.geomesa.utils.collection.CircularByteQueue.array CircularByteQueue.this.array
144 2802 3946 - 3947 Literal <nosymbol> 0
144 2803 3965 - 3983 Apply scala.Int.- CircularByteQueue.this.length.-(tailBytes)
144 2804 3922 - 3984 Apply java.lang.System.arraycopy java.lang.System.arraycopy(CircularByteQueue.this.array, 0, tmp, tailBytes, CircularByteQueue.this.length.-(tailBytes))
146 2806 3999 - 4010 Apply org.locationtech.geomesa.utils.collection.CircularByteQueue.array_= CircularByteQueue.this.array_=(tmp)
147 2807 4017 - 4026 Apply org.locationtech.geomesa.utils.collection.CircularByteQueue.start_= CircularByteQueue.this.start_=(0)