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

bernardladenthin / BitcoinAddressFinder / #308

28 Apr 2025 10:01PM UTC coverage: 68.174% (-0.7%) from 68.832%
#308

push

bernardladenthin
Refactor OpenCL handling for device endianness and dynamic platform selection

22 of 76 new or added lines in 10 files covered. (28.95%)

1 existing line in 1 file now uncovered.

1221 of 1791 relevant lines covered (68.17%)

0.68 hits per line

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

0.0
/src/main/java/net/ladenthin/bitcoinaddressfinder/OpenClTask.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 com.google.common.annotations.VisibleForTesting;
22
import java.math.BigInteger;
23
import java.nio.ByteBuffer;
24
import java.nio.ByteOrder;
25
import net.ladenthin.bitcoinaddressfinder.configuration.CProducer;
26
import static org.jocl.CL.CL_MEM_READ_ONLY;
27
import static org.jocl.CL.CL_MEM_USE_HOST_PTR;
28
import static org.jocl.CL.CL_MEM_WRITE_ONLY;
29
import static org.jocl.CL.CL_TRUE;
30
import static org.jocl.CL.clCreateBuffer;
31
import static org.jocl.CL.clEnqueueNDRangeKernel;
32
import static org.jocl.CL.clEnqueueReadBuffer;
33
import static org.jocl.CL.clEnqueueWriteBuffer;
34
import static org.jocl.CL.clFinish;
35
import static org.jocl.CL.clReleaseMemObject;
36
import static org.jocl.CL.clSetKernelArg;
37
import org.jocl.Pointer;
38
import org.jocl.Sizeof;
39
import org.jocl.cl_command_queue;
40
import org.jocl.cl_context;
41
import org.jocl.cl_kernel;
42
import org.jocl.cl_mem;
43
import org.slf4j.Logger;
44
import org.slf4j.LoggerFactory;
45

46
public class OpenClTask {
47

48
    protected Logger logger = LoggerFactory.getLogger(this.getClass());
×
49
    
50
    /**
51
     * I din't know which is better.
52
     */
53
    private static final boolean USE_HOST_PTR = false;
54
    
55
    private final CProducer cProducer;
56

57
    private final cl_context context;
58
    private final ByteOrder clByteOrder;
59
    private final ByteBuffer srcByteBuffer;
60
    private final Pointer srcPointer;
61

62
    private final cl_mem srcMem;
63
    private final BitHelper bitHelper;
64
    private final ByteBufferUtility byteBufferUtility;
65
    private final BigInteger maxPrivateKeyForBatchSize;
66

67
    // Only available after init
NEW
68
    public OpenClTask(cl_context context, ByteOrder clByteOrder, CProducer cProducer, BitHelper bitHelper, ByteBufferUtility byteBufferUtility) {
×
69
        this.context = context;
×
70
        this.cProducer = cProducer;
×
71
        this.bitHelper = bitHelper;
×
72
        this.byteBufferUtility = byteBufferUtility;
×
NEW
73
        this.clByteOrder = clByteOrder;
×
74

75
        int srcSizeInBytes = getSrcSizeInBytes();
×
76
        maxPrivateKeyForBatchSize = KeyUtility.getMaxPrivateKeyForBatchSize(cProducer.batchSizeInBits);
×
77
        srcByteBuffer = ByteBuffer.allocateDirect(srcSizeInBytes);
×
78
        srcPointer = Pointer.to(srcByteBuffer);
×
79
        srcMem = clCreateBuffer(
×
80
                context,
81
                CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR,
82
                srcSizeInBytes,
83
                srcPointer,
84
                null
85
        );
86
    }
×
87

88
    public int getSrcSizeInBytes() {
89
        return PublicKeyBytes.PRIVATE_KEY_MAX_NUM_BYTES;
×
90
    }
91

92
    public long getDstSizeInBytes() {
93
        return (long) PublicKeyBytes.TWO_COORDINATES_NUM_BYTES * bitHelper.convertBitsToSize(cProducer.batchSizeInBits);
×
94
    }
95

96
    public void setSrcPrivateKeyChunk(BigInteger privateKeyBase) {
97
        if (KeyUtility.isInvalidWithBatchSize(privateKeyBase, maxPrivateKeyForBatchSize)) {
×
98
            throw new PrivateKeyTooLargeException(privateKeyBase, maxPrivateKeyForBatchSize, cProducer.batchSizeInBits);
×
99
        }
100

101
        // BigInteger.toByteArray() always returns a big-endian (MSB-first) representation, 
102
        // meaning the most significant byte (MSB) comes first.
103
        // Therefore, the source format is always Big Endian.
UNCOV
104
        byte[] byteArray = byteBufferUtility.bigIntegerToBytes(privateKeyBase);
×
NEW
105
        EndiannessConverter endiannessConverter = new EndiannessConverter(ByteOrder.BIG_ENDIAN, clByteOrder, byteBufferUtility);
×
NEW
106
        endiannessConverter.convertEndian(byteArray);
×
107
        byteBufferUtility.putToByteBuffer(srcByteBuffer, byteArray);
×
108
    }
×
109
    
110
    @VisibleForTesting
111
    public ByteBuffer getSrcByteBuffer() {
112
        return srcByteBuffer;
×
113
    }
114
    
115
    @VisibleForTesting
116
    public ByteOrder getClByteOrder() {
NEW
117
        return clByteOrder;
×
118
    }
119

120
    public ByteBuffer executeKernel(cl_kernel kernel, cl_command_queue commandQueue) {
121
        final long dstSizeInBytes = getDstSizeInBytes();
×
122
        // allocate a new dst buffer that a clone afterwards is not necessary
123
        final ByteBuffer dstByteBuffer = ByteBuffer.allocateDirect(ByteBufferUtility.ensureByteBufferCapacityFitsInt(dstSizeInBytes));
×
124
        final Pointer dstPointer = Pointer.to(dstByteBuffer);
×
125
        final cl_mem dstMem;
126
        if (USE_HOST_PTR) {
127
            dstMem = clCreateBuffer(context,
128
                    CL_MEM_USE_HOST_PTR, dstSizeInBytes,
129
                    dstPointer,
130
                    null
131
            );
132
        } else {
133
            dstMem = clCreateBuffer(context,
×
134
                    CL_MEM_WRITE_ONLY, dstSizeInBytes,
135
                    null,
136
                    null
137
            );
138
        }
139

140
        // Set the arguments for the kernel
141
        clSetKernelArg(kernel, 0, Sizeof.cl_mem, Pointer.to(dstMem));
×
142
        clSetKernelArg(kernel, 1, Sizeof.cl_mem, Pointer.to(srcMem));
×
143

144
        // Set the work-item dimensions
145
        long global_work_size[] = new long[]{bitHelper.convertBitsToSize(cProducer.batchSizeInBits)};
×
146
        long localWorkSize[] = null; // new long[]{1}; // enabling the system to choose the work-group size.
×
147
        int workDim = 1;
×
148

149
        {
150
            // write src buffer
151
            clEnqueueWriteBuffer(
×
152
                    commandQueue,
153
                    srcMem,
154
                    CL_TRUE,
155
                    0,
156
                    getSrcSizeInBytes(),
×
157
                    srcPointer,
158
                    0,
159
                    null,
160
                    null
161
            );
162
            clFinish(commandQueue);
×
163
        }
164
        {
165
            // execute the kernel
166
            long beforeExecute = System.currentTimeMillis();
×
167
            clEnqueueNDRangeKernel(
×
168
                    commandQueue,
169
                    kernel,
170
                    workDim,
171
                    null,
172
                    global_work_size,
173
                    localWorkSize,
174
                    0,
175
                    null,
176
                    null
177
            );
178
            clFinish(commandQueue);
×
179

180
            long afterExecute = System.currentTimeMillis();
×
181
            
182
            if (logger.isTraceEnabled()) {
×
183
                logger.trace("Executed OpenCL kernel in " + (afterExecute - beforeExecute) + "ms");
×
184
            }
185
        }
186
        {
187
            // read the dst buffer
188
            long beforeRead = System.currentTimeMillis();
×
189

190
            clEnqueueReadBuffer(commandQueue,
×
191
                    dstMem,
192
                    CL_TRUE,
193
                    0,
194
                    dstSizeInBytes,
195
                    dstPointer,
196
                    0,
197
                    null,
198
                    null
199
            );
200
            clFinish(commandQueue);
×
201
            clReleaseMemObject(dstMem);
×
202

203
            long afterRead = System.currentTimeMillis();
×
204
            if (logger.isTraceEnabled()) {
×
205
                logger.trace("Read OpenCL data "+((dstSizeInBytes / 1024) / 1024) + "Mb in " + (afterRead - beforeRead) + "ms");
×
206
            }
207
        }
208
        return dstByteBuffer;
×
209
    }
210

211
    public void releaseCl() {
212
        clReleaseMemObject(srcMem);
×
213
    }
×
214

215
    /**
216
     * https://stackoverflow.com/questions/3366925/deep-copy-duplicate-of-javas-bytebuffer/4074089
217
     */
218
    private static ByteBuffer cloneByteBuffer(final ByteBuffer original) {
219
        // Create clone with same capacity as original.
220
        final ByteBuffer clone = (original.isDirect())
×
221
                ? ByteBuffer.allocateDirect(original.capacity())
×
222
                : ByteBuffer.allocate(original.capacity());
×
223

224
        // Create a read-only copy of the original.
225
        // This allows reading from the original without modifying it.
226
        final ByteBuffer readOnlyCopy = original.asReadOnlyBuffer();
×
227

228
        // Flip and read from the original.
229
        readOnlyCopy.flip();
×
230
        clone.put(readOnlyCopy);
×
231

232
        return clone;
×
233
    }
234

235
}
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