• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

bernardladenthin / BitcoinAddressFinder / #305

19 Apr 2025 11:43PM UTC coverage: 68.442% (+0.9%) from 67.58%
#305

push

bernardladenthin
Refactor private key validation and serialization; extract logic to KeyUtility and ByteBufferUtility, improve tests

51 of 65 new or added lines in 9 files covered. (78.46%)

2 existing lines in 2 files now uncovered.

1195 of 1746 relevant lines covered (68.44%)

0.68 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.21
/src/main/java/net/ladenthin/bitcoinaddressfinder/ByteBufferUtility.java
1
// @formatter:off
2
/**
3
 * Copyright 2020 Bernard Ladenthin bernard.ladenthin@gmail.com
4
 *
5
 * Licensed under the Apache License, Version 2.0 (the "License");
6
 * you may not use this file except in compliance with the License.
7
 * You may obtain a copy of the License at
8
 *
9
 *    http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 *
17
 */
18
// @formatter:on
19
package net.ladenthin.bitcoinaddressfinder;
20

21
import java.math.BigInteger;
22
import java.nio.ByteBuffer;
23
import java.util.Arrays;
24
import jdk.internal.misc.Unsafe;
25
import org.bouncycastle.util.encoders.Hex;
26

27
public class ByteBufferUtility {
28
    
29
    /**
30
     * Decide between {@link java.nio.DirectByteBuffer} and {@link java.nio.HeapByteBuffer}.
31
     */
32
    private final boolean allocateDirect;
33

34
    public ByteBufferUtility(boolean allocateDirect) {
1✔
35
        this.allocateDirect = allocateDirect;
1✔
36
    }
1✔
37
    
38
    /**
39
     * ATTENTION: The {@link Unsafe#getUnsafe} can throw an {@link java.lang.IllegalAccessError}.
40
     * https://stackoverflow.com/questions/8462200/examples-of-forcing-freeing-of-native-memory-direct-bytebuffer-has-allocated-us
41
     * https://stackoverflow.com/questions/13003871/how-do-i-get-the-instance-of-sun-misc-unsafe
42
     * https://stackoverflow.com/questions/29301755/got-securityexception-in-java
43
     * https://bugs.openjdk.org/browse/JDK-8171377
44
     * @param byteBuffer nullable, the ByteBuffer to free 
45
     */
46
    public void freeByteBuffer(ByteBuffer byteBuffer) {
47
        if (byteBuffer == null) {
1✔
48
            return;
1✔
49
        }
50

51
        if (!byteBuffer.isDirect()) {
1✔
52
            return;
1✔
53
        }
54

55
        Unsafe u = Unsafe.getUnsafe();
1✔
56
        // https://bugs.openjdk.org/browse/JDK-8171377
57
        // https://openjdk.org/jeps/8323072
58
        // https://stackoverflow.com/questions/3496508/deallocating-direct-buffer-native-memory-in-java-for-jogl/26777380
59
        u.invokeCleaner(byteBuffer);
1✔
60
    }
1✔
61
    
62
    // <editor-fold defaultstate="collapsed" desc="ByteBuffer byte array conversion">
63
    public byte[] byteBufferToBytes(ByteBuffer byteBuffer) {
64
        byte[] bytes = new byte[byteBuffer.remaining()];
1✔
65
        byteBuffer.get(bytes);
1✔
66
        byteBuffer.rewind();
1✔
67
        return bytes;
1✔
68
    }
69
    
70
    public ByteBuffer byteArrayToByteBuffer(byte[] bytes) {
71
        if (allocateDirect) { 
1✔
72
            return byteArrayToByteBufferAllocatedDirect(bytes);
1✔
73
        } else {
74
            return byteArrayToByteBufferWrapped(bytes);
1✔
75
        }
76
    }
77

78
    private ByteBuffer byteArrayToByteBufferWrapped(byte[] bytes) {
79
        // wrap() delivers a buffer which is already flipped
80
        ByteBuffer wrap = ByteBuffer.wrap(bytes);
1✔
81
        return wrap;
1✔
82
    }
83

84
    private ByteBuffer byteArrayToByteBufferAllocatedDirect(byte[] bytes) {
85
        ByteBuffer key = ByteBuffer.allocateDirect(bytes.length);
1✔
86
        key.put(bytes).flip();
1✔
87
        return key;
1✔
88
    }
89
    // </editor-fold>
90
    
91
    // <editor-fold defaultstate="collapsed" desc="ByteBuffer Hex conversion">
92
    public String getHexFromByteBuffer(ByteBuffer byteBuffer) {
93
        byte[] array = byteBufferToBytes(byteBuffer);
1✔
94
        String hexString = Hex.toHexString(array);
1✔
95
        return hexString;
1✔
96
    }
97

98
    public ByteBuffer getByteBufferFromHex(String hex) {
99
        byte[] decoded = Hex.decode(hex);
1✔
100
        // wrap() delivers a buffer which is already flipped
101
        final ByteBuffer byteBuffer = byteArrayToByteBuffer(decoded);
1✔
102
        return byteBuffer;
1✔
103
    }
104
    // </editor-fold>
105
    
106
    // <editor-fold defaultstate="collapsed" desc="ensureByteBufferCapacityFitsInt">
107
    /**
108
    * Validates that the given capacity fits within Java's ByteBuffer limit.
109
    * 
110
    * @param capacity the desired buffer capacity in bytes
111
    * @return the same value as an int, if within bounds
112
    * @throws IllegalArgumentException if capacity exceeds Integer.MAX_VALUE or is negative
113
    */
114
   public static int ensureByteBufferCapacityFitsInt(long capacity) {
115
       if (capacity < 0) {
1✔
116
           throw new IllegalArgumentException("Capacity must not be negative: " + capacity);
1✔
117
       }
118
       if (capacity > Integer.MAX_VALUE) {
1✔
119
           throw new IllegalArgumentException("Capacity exceeds maximum ByteBuffer limit: " + capacity);
1✔
120
       }
121
       return (int) capacity;
1✔
122
   }
123
    // </editor-fold>
124
   
125
    // <editor-fold defaultstate="collapsed" desc="allocateByteBufferDirectStrict (enforce direct allocation)">
126
    /**
127
     * Allocates a {@link ByteBuffer} strictly using {@link ByteBuffer#allocateDirect(int)}.
128
     * <p>
129
     * This method enforces that {@code allocateDirect == true}.
130
     * It throws an {@link IllegalStateException} if heap allocation is enabled.
131
     * Useful when native memory access is required, e.g. for OpenCL interop.
132
     *
133
     * @param capacity the number of bytes to allocate
134
     * @return a direct {@link ByteBuffer}
135
     * @throws IllegalStateException if not configured for direct allocation
136
     */
137
    public ByteBuffer allocateByteBufferDirectStrict(int capacity) {
138
        if (!allocateDirect) {
1✔
139
            throw new IllegalStateException("Direct allocation requested, but allocateDirect is false.");
1✔
140
        }
141
        return ByteBuffer.allocateDirect(capacity);
1✔
142
    }
143
    // </editor-fold>
144
    
145
    /**
146
     * https://bitbucket.org/connect2id/nimbus-srp/pull-requests/6/remove-leading-zero-byte-when-converting/diff
147
     * Converts a BigInteger into a byte array ignoring the sign of the
148
     * BigInteger, according to SRP specification
149
     *
150
     * @param bigInteger BigInteger, must not be null
151
     *
152
     * @return byte array (leading byte is always != 0), empty array if
153
     * BigInteger is zero.
154
     */
155
    public static byte[] bigIntegerToBytes(final BigInteger bigInteger) {
156
        byte[] bytes = bigInteger.toByteArray();
1✔
157
        if (bytes[0] == 0) {
1✔
158
            return Arrays.copyOfRange(bytes, 1, bytes.length);
1✔
159
        }
160
        return bytes;
1✔
161
    }
162
    
163
    /**
164
    * Writes a BigInteger into a ByteBuffer in Most-Significant-Byte to Least-Significant-Byte order.
165
    * The input BigInteger is converted to a 32-byte array. Leading zeros are stripped or added to ensure exactly 32 bytes.
166
    * This is important for OpenCL, which expects keys in LSB order, written as raw bytes.
167
    *
168
    * @param buffer The ByteBuffer to write to. Must have at least 32 bytes capacity.
169
    * @param byteArray The byte array to write.
170
    */
171
   public static void putToByteBufferAsMSBtoLSB(ByteBuffer buffer, byte[] byteArray) {
172
       reverse(byteArray);
1✔
173
       buffer.clear();
1✔
174
       buffer.put(byteArray, 0, byteArray.length);
1✔
175
       buffer.rewind();
1✔
176
   }
1✔
177
   
178
    private final static boolean USE_XOR_SWAP = false;
179
    
180
    /**
181
     * https://stackoverflow.com/questions/12893758/how-to-reverse-the-byte-array-in-java
182
     */
183
    public static void reverse(byte[] array) {
184
        if (array == null) {
1✔
NEW
185
            return;
×
186
        }
187
        if (USE_XOR_SWAP) {
188
            int len = array.length;
189
            for (int i = 0; i < len / 2; i++) {
190
                array[i] ^= array[len - i - 1];
191
                array[len - i - 1] ^= array[i];
192
                array[i] ^= array[len - i - 1];
193
            }
194
        } else {
195
            int i = 0;
1✔
196
            int j = array.length - 1;
1✔
197
            byte tmp;
198
            while (j > i) {
1✔
199
                tmp = array[j];
1✔
200
                array[j] = array[i];
1✔
201
                array[i] = tmp;
1✔
202
                j--;
1✔
203
                i++;
1✔
204
            }
205
        }
206
    }
1✔
207

208
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc