aboutsummaryrefslogtreecommitdiffstats
path: root/java_base/org/jau/io/Buffers.java
blob: a93095842419c7a625ca56850c8270d565bbb77f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
/**
 * Author: Sven Gothel <sgothel@jausoft.com>
 * Copyright (c) 2021 Gothel Software e.K.
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
package org.jau.io;

import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.CharBuffer;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.LongBuffer;
import java.nio.ShortBuffer;

import org.jau.lang.UnsafeUtil;
import org.jau.sys.Debug;
import org.jau.sys.PlatformProps;

/**
 * Utility methods allowing easy {@link java.nio.Buffer} manipulations.
 */
public class Buffers {
    static final boolean DEBUG = Debug.debug("NioUtil");

    public static final int SIZEOF_BYTE     = 1;
    public static final int SIZEOF_SHORT    = 2;
    public static final int SIZEOF_CHAR     = 2;
    public static final int SIZEOF_INT      = 4;
    public static final int SIZEOF_FLOAT    = 4;
    public static final int SIZEOF_LONG     = 8;
    public static final int SIZEOF_DOUBLE   = 8;

    private Buffers() {}

    /**
     * Allocates a new direct ByteBuffer with the specified number of
     * elements. The returned buffer will have its byte order set to
     * the host platform's native byte order.
     */
    public static ByteBuffer newDirectByteBuffer(final int size) {
        return ByteBuffer.allocateDirect( size ).order( ByteOrder.nativeOrder() );
    }

    /**
     * Helper routine to set a ByteBuffer to the native byte order, if
     * that operation is supported by the underlying NIO
     * implementation.
     */
    public static ByteBuffer nativeOrder(final ByteBuffer buf) {
        return buf.order(ByteOrder.nativeOrder());
    }

    /**
     * Helper routine to tell whether a buffer is direct or not. Null
     * pointers <b>are</b> considered direct.
     */
    public static boolean isDirect(final Object buf) {
        if (buf == null) {
            return true;
        }
        if (buf instanceof Buffer) {
            return ((Buffer) buf).isDirect();
        }
        throw new IllegalArgumentException("Unexpected buffer type " + buf.getClass().getName());
    }

    /**
     * Helper routine to get the Buffer byte offset by taking into
     * account the Buffer position and the underlying type.
     * This is the total offset for Direct Buffers.
     *
     * Return value is of type `long` only to cover the `int` multiple of the position and element type size.<br/>
     * For ByteBuffer, the return value can be safely cast to `int`.
     */
    public static long getDirectBufferByteOffset(final Object buf) {
        if (buf == null) {
            return 0;
        }
        if (buf instanceof Buffer) {
            final long pos = ((Buffer) buf).position();
            if (buf instanceof ByteBuffer) {
                return pos;
            } else if (buf instanceof FloatBuffer) {
                return pos * SIZEOF_FLOAT;
            } else if (buf instanceof IntBuffer) {
                return pos * SIZEOF_INT;
            } else if (buf instanceof ShortBuffer) {
                return pos * SIZEOF_SHORT;
            } else if (buf instanceof DoubleBuffer) {
                return pos * SIZEOF_DOUBLE;
            } else if (buf instanceof LongBuffer) {
                return pos * SIZEOF_LONG;
            } else if (buf instanceof CharBuffer) {
                return pos * SIZEOF_CHAR;
            }
        }
        throw new IllegalArgumentException("Disallowed array backing store type in buffer " + buf.getClass().getName());
    }

    /**
     * Helper routine to get the Buffer byte limit by taking into
     * account the Buffer limit and the underlying type.
     * This is the total limit for Direct Buffers.
     *
     * Return value is of type `long` only to cover the `int` multiple of the position and element type size.<br/>
     * For ByteBuffer, the return value can be safely cast to `int`.
     */
    public static long getDirectBufferByteLimit(final Object buf) {
        if (buf == null) {
            return 0;
        }
        if (buf instanceof Buffer) {
            final long limit = ((Buffer) buf).limit();
            if (buf instanceof ByteBuffer) {
                return limit;
            } else if (buf instanceof FloatBuffer) {
                return limit * SIZEOF_FLOAT;
            } else if (buf instanceof IntBuffer) {
                return limit * SIZEOF_INT;
            } else if (buf instanceof ShortBuffer) {
                return limit * SIZEOF_SHORT;
            } else if (buf instanceof DoubleBuffer) {
                return limit * SIZEOF_DOUBLE;
            } else if (buf instanceof LongBuffer) {
                return limit * SIZEOF_LONG;
            } else if (buf instanceof CharBuffer) {
                return limit * SIZEOF_CHAR;
            }
        }
        throw new IllegalArgumentException("Disallowed array backing store type in buffer " + buf.getClass().getName());
    }

    /**
     * Access to NIO {@link sun.misc.Cleaner}, allowing caller to deterministically clean a given {@link sun.nio.ch.DirectBuffer}.
     */
    public static class Cleaner {
        private static final boolean hasCleaner;
        /** OK to be lazy on thread synchronization, just for early out **/
        private static volatile boolean cleanerError;
        static {
            hasCleaner = UnsafeUtil.hasInvokeCleaner();
            cleanerError = !hasCleaner;
            if( DEBUG ) {
                System.err.println("Buffers.Cleaner.init: hasCleaner: "+hasCleaner+", cleanerError "+cleanerError);
            }
        }

        /**
         * If {@code b} is an direct NIO buffer, i.e {@link sun.nio.ch.DirectBuffer},
         * calls it's {@link sun.misc.Cleaner} instance {@code clean()} method once.
         * @return {@code true} if successful, otherwise {@code false}.
         */
        public static boolean clean(final ByteBuffer bb) {
            if( !hasCleaner && ( cleanerError || !bb.isDirect() ) ) {
                return false;
            }
            if( PlatformProps.JAVA_9 ) {
                UnsafeUtil.invokeCleaner(bb);
            } else {
                return false;
            }
            return true;
        }
    }

}