AncillaryDataSupport.java
/*
* junixsocket
*
* Copyright 2009-2024 Christian Kohlschütter
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.newsclub.net.unix;
import java.io.Closeable;
import java.io.FileDescriptor;
import java.io.IOException;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.kohlschutter.annotations.compiletime.SuppressFBWarnings;
final class AncillaryDataSupport implements Closeable {
private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.allocate(0);
private static final FileDescriptor[] NO_FILE_DESCRIPTORS = new FileDescriptor[0];
private static final int MIN_ANCBUF_LEN = NativeUnixSocket.isLoaded() ? NativeUnixSocket
.ancillaryBufMinLen() : 0;
private final Map<FileDescriptor, Integer> openReceivedFileDescriptors = Collections
.synchronizedMap(new HashMap<>());
private final List<FileDescriptor[]> receivedFileDescriptors = Collections.synchronizedList(
new ArrayList<>());
// referenced from native code
private ByteBuffer ancillaryReceiveBuffer = EMPTY_BUFFER;
// referenced from native code
@SuppressFBWarnings("URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
int[] pendingFileDescriptors = null;
private int[] tipcErrorInfo = null;
private int[] tipcDestName = null;
// referenced from native code
void setTipcErrorInfo(int errorCode, int dataLength) {
if (errorCode == 0 && dataLength == 0) {
tipcErrorInfo = null;
} else {
tipcErrorInfo = new int[] {errorCode, dataLength};
}
}
int[] getTIPCErrorInfo() {
int[] info = tipcErrorInfo;
tipcErrorInfo = null;
return info;
}
void setTipcDestName(int a, int b, int c) {
if (a == 0 && b == 0 && c == 0) {
this.tipcDestName = null;
} else {
this.tipcDestName = new int[] {a, b, c};
}
}
int[] getTIPCDestName() {
int[] addr = tipcDestName;
tipcDestName = null;
return addr;
}
int getAncillaryReceiveBufferSize() {
return ancillaryReceiveBuffer.capacity();
}
void setAncillaryReceiveBufferSize(int size) {
if (size == ancillaryReceiveBuffer.capacity()) {
return;
} else if (size <= 0) {
this.ancillaryReceiveBuffer = EMPTY_BUFFER;
} else {
setAncillaryReceiveBufferSize0(Math.max(256, Math.min(MIN_ANCBUF_LEN, size)));
}
}
void setAncillaryReceiveBufferSize0(int size) {
this.ancillaryReceiveBuffer = ByteBuffer.allocateDirect(size);
}
public void ensureAncillaryReceiveBufferSize(int minSize) {
if (minSize <= 0) {
return;
}
if (ancillaryReceiveBuffer.capacity() < minSize) {
setAncillaryReceiveBufferSize(minSize);
}
}
// called from native code
void receiveFileDescriptors(int[] fds) throws IOException {
if (fds == null || fds.length == 0) {
return;
}
final int fdsLength = fds.length;
FileDescriptor[] descriptors = new FileDescriptor[fdsLength];
for (int i = 0; i < fdsLength; i++) {
final FileDescriptor fdesc = new FileDescriptor();
NativeUnixSocket.initFD(fdesc, fds[i]);
descriptors[i] = fdesc;
openReceivedFileDescriptors.put(fdesc, fds[i]);
final Closeable cleanup = new Closeable() {
@Override
public void close() throws IOException {
openReceivedFileDescriptors.remove(fdesc);
}
};
try {
NativeUnixSocket.attachCloseable(fdesc, cleanup);
} catch (SocketException e) {
// ignore (cannot attach)
}
}
this.receivedFileDescriptors.add(descriptors);
}
void clearReceivedFileDescriptors() {
receivedFileDescriptors.clear();
}
FileDescriptor[] getReceivedFileDescriptors() {
if (receivedFileDescriptors.isEmpty()) {
return NO_FILE_DESCRIPTORS;
}
List<FileDescriptor[]> copy = new ArrayList<>(receivedFileDescriptors);
if (copy.isEmpty()) {
return NO_FILE_DESCRIPTORS;
}
receivedFileDescriptors.removeAll(copy);
int count = 0;
for (FileDescriptor[] fds : copy) {
count += fds.length;
}
if (count == 0) {
return NO_FILE_DESCRIPTORS;
}
FileDescriptor[] oneArray = new FileDescriptor[count];
int offset = 0;
for (FileDescriptor[] fds : copy) {
System.arraycopy(fds, 0, oneArray, offset, fds.length);
offset += fds.length;
}
return oneArray;
}
void setOutboundFileDescriptors(int[] fds) {
this.pendingFileDescriptors = (fds == null || fds.length == 0) ? null : fds;
}
boolean hasOutboundFileDescriptors() {
return this.pendingFileDescriptors != null;
}
void setOutboundFileDescriptors(FileDescriptor... fdescs) throws IOException {
final int[] fds;
if (fdescs == null || fdescs.length == 0) {
fds = null;
} else {
final int numFdescs = fdescs.length;
fds = new int[numFdescs];
for (int i = 0; i < numFdescs; i++) {
FileDescriptor fdesc = fdescs[i];
fds[i] = NativeUnixSocket.getFD(fdesc);
}
}
this.setOutboundFileDescriptors(fds);
}
@Override
public void close() {
synchronized (openReceivedFileDescriptors) {
for (FileDescriptor desc : openReceivedFileDescriptors.keySet()) {
if (desc.valid()) {
try {
NativeUnixSocket.close(desc);
} catch (Exception e) {
// ignore
}
}
}
}
}
}