All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hcl.domino.misc.StructureSupport Maven / Gradle / Ivy

There is a newer version: 1.44.0
Show newest version
/*
 * ==========================================================================
 * Copyright (C) 2019-2022 HCL America, Inc. ( http://www.hcl.com/ )
 *                            All rights reserved.
 * ==========================================================================
 * 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 .
 *
 * 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 com.hcl.domino.misc;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.ByteBuffer;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.IntConsumer;
import java.util.function.LongConsumer;
import java.util.stream.LongStream;
import java.util.stream.Stream;

import com.hcl.domino.data.NativeItemCoder;
import com.hcl.domino.data.NativeItemCoder.LmbcsVariant;
import com.hcl.domino.formula.FormulaCompiler;
import com.hcl.domino.richtext.structures.ListStructure;
import com.hcl.domino.richtext.structures.MemoryStructureWrapperService;
import com.hcl.domino.richtext.structures.ResizableMemoryStructure;

/**
 * @author Jesse Gallagher
 * @since 1.0.24
 */
public enum StructureSupport {
  ;

  public static String extractCompiledFormula(final ResizableMemoryStructure struct, final long preLen, final long len) {
    if (len == 0) {
      return ""; //$NON-NLS-1$
    }
    if (preLen > Integer.MAX_VALUE) {
      throw new UnsupportedOperationException(
          MessageFormat.format("Unable to extract a formula value with offset larger than {0} bytes", Integer.MAX_VALUE));
    }
    if (len > Integer.MAX_VALUE) {
      throw new UnsupportedOperationException(
          MessageFormat.format("Unable to extract a formula value larger than {0} bytes", Integer.MAX_VALUE));
    }

    final ByteBuffer buf = struct.getVariableData();
    if(preLen > buf.limit()) {
      return ""; //$NON-NLS-1$
    }
    buf.position((int) Math.max(0, preLen));
    final byte[] compiled = new byte[(int) len];
    buf.get(compiled);
    return FormulaCompiler.get().decompile(compiled);
  }

  /**
   * Extracts a "packed" string value from the structure. For this use, "packed"
   * means that the string
   * data is exactly as long as {@code len}, with no null terminator or WORD-size
   * padding.
   *
   * @param struct the structure to extract from
   * @param preLen the number of bytes before the value to extract
   * @param len    the size of the stored value
   * @return the extracted string value
   */
  public static String extractStringValue(final ResizableMemoryStructure struct, final long preLen, final long len) {
    if (len == 0) {
      return ""; //$NON-NLS-1$
    }
    if (preLen > Integer.MAX_VALUE) {
      throw new UnsupportedOperationException(
          MessageFormat.format("Unable to extract a string value with offset larger than {0} bytes", Integer.MAX_VALUE));
    }
    if (len > Integer.MAX_VALUE) {
      throw new UnsupportedOperationException(
          MessageFormat.format("Unable to extract a string value larger than {0} bytes", Integer.MAX_VALUE));
    }

    final ByteBuffer buf = struct.getVariableData();
    if(preLen > buf.limit()) {
      return ""; //$NON-NLS-1$
    }
    buf.position((int) Math.max(0, preLen));
    // Account for cases where the logical length is incorrect
    final int resolvedLen = Math.min((int)len, buf.remaining());
    final byte[] lmbcs = new byte[resolvedLen];
    buf.get(lmbcs);
    return new String(lmbcs, NativeItemCoder.get().getLmbcsCharset());
  }

  /**
   * Extracts a list of "packed" string values from the structure. For this use, "packed"
   * means that the string data is exactly as long as each of the lengths in {@code lengths},
   * with no null terminator or WORD-size padding.
   *
   * @param struct  the structure to extract from
   * @param preLen  the number of bytes before the value to extract
   * @param lengths the lengths of the string values
   * @return the extracted string values
   * @since 1.0.38
   */
  public static List extractStringValues(final ResizableMemoryStructure struct, final long preLen, final long[] lengths) {
    if (lengths.length == 0) {
      return Collections.emptyList();
    }
    if (preLen > Integer.MAX_VALUE) {
      throw new UnsupportedOperationException(
          MessageFormat.format("Unable to extract a string value with offset larger than {0} bytes", Integer.MAX_VALUE));
    }

    final ByteBuffer buf = struct.getVariableData();
    if(preLen > buf.limit()) {
      return Collections.emptyList();
    }
    buf.position((int) Math.max(0, preLen));
    List result = new ArrayList<>(lengths.length);
    for(long len : lengths) {
      final byte[] lmbcs = new byte[(int) len];
      buf.get(lmbcs);
      result.add(new String(lmbcs, NativeItemCoder.get().getLmbcsCharset())); 
    }
    return result;
  }

  /**
   * Extracts an "unpacked" string value from the structure. For this use,
   * "unpacked" means that:
   * 
    *
  • The string is null-terminated
  • *
  • If the length of the string plus its terminator is odd, there is another * packing null * value at the end
  • *
* * @param struct the structure to extract from * @param preLen the number of bytes before the value to extract * @param len the size of the stored value * @return the extracted string value */ public static String extractStringValueUnpacked(final ResizableMemoryStructure struct, final long preLen, final long len) { if (len == 0 || len == -1) { return ""; //$NON-NLS-1$ } if (preLen > Integer.MAX_VALUE) { throw new UnsupportedOperationException( MessageFormat.format("Unable to extract a string value with offset larger than {0} bytes", Integer.MAX_VALUE)); } if (len > Integer.MAX_VALUE) { throw new UnsupportedOperationException( MessageFormat.format("Unable to extract a string value larger than {0} bytes", Integer.MAX_VALUE)); } final ByteBuffer buf = struct.getVariableData(); if(preLen > buf.limit()) { return ""; //$NON-NLS-1$ } buf.position((int) Math.max(0, preLen)); final byte[] lmbcs = new byte[(int) len]; buf.get(lmbcs); int nullPos = lmbcs.length - 1; while ((lmbcs[nullPos] == 0 || lmbcs[nullPos] == -1) && nullPos > 0) { nullPos--; } return new String(lmbcs, 0, nullPos + 1, NativeItemCoder.get().getLmbcsCharset()); } /** * Extracts a "padded" string value from the structure. For this use, "padded" * means that: *
    *
  • The string is not null-terminated
  • *
  • If the length of the string is odd, there is another packing null value * at the end
  • *
* * @param struct the structure to extract from * @param preLen the number of bytes before the value to extract * @param len the size of the stored value * @return the extracted string value */ public static String extractStringValueWordPadded(final ResizableMemoryStructure struct, final long preLen, final long len) { if (len == 0) { return ""; //$NON-NLS-1$ } if (preLen > Integer.MAX_VALUE) { throw new UnsupportedOperationException( MessageFormat.format("Unable to extract a string value with offset larger than {0} bytes", Integer.MAX_VALUE)); } if (len > Integer.MAX_VALUE) { throw new UnsupportedOperationException( MessageFormat.format("Unable to extract a string value larger than {0} bytes", Integer.MAX_VALUE)); } final ByteBuffer buf = struct.getVariableData(); if(preLen > buf.limit()) { return ""; //$NON-NLS-1$ } buf.position((int) Math.max(0, preLen)); final byte[] lmbcs = new byte[(int) len]; buf.get(lmbcs); if (lmbcs[lmbcs.length - 1] == '\0' || lmbcs[lmbcs.length - 1] == -1) { return new String(lmbcs, 0, lmbcs.length - 1, NativeItemCoder.get().getLmbcsCharset()); } else { return new String(lmbcs, NativeItemCoder.get().getLmbcsCharset()); } } /** * Extracts a list of strings stored using the {@link ListStructure} format. * * @param struct the structure to read from * @param preLen the number of bytes before the value to extract * @return a {@link List} of extracted strings * @since 1.0.38 */ public static List extractStringListStructure(ResizableMemoryStructure struct, int preLen) { ByteBuffer buf = struct.getVariableData(); buf.position(Math.max(0, preLen)); int count = Short.toUnsignedInt(buf.getShort()); int[] sizes = new int[count]; for(int i = 0; i < count; i++) { sizes[i] = Short.toUnsignedInt(buf.getShort()); } List result = new ArrayList<>(); for(int i = 0; i < count; i++) { // Read sizes[i] bytes as LMBCS byte[] lmbcs = new byte[sizes[i]]; buf.get(lmbcs); result.add(new String(lmbcs, NativeItemCoder.get().getLmbcsCharset())); } return result; } /** * Extracts raw byte data from a structure. * * @param struct the structure to extract from * @param preLen the number of bytes before the value to extract * @param len the size of the stored value * @return the extracted byte data * @since 1.0.43 */ public static byte[] extractByteArray(final ResizableMemoryStructure struct, final long preLen, final long len) { if (len == 0) { return new byte[0]; } if (preLen > Integer.MAX_VALUE) { throw new UnsupportedOperationException( MessageFormat.format("Unable to extract a byte value with offset larger than {0} bytes", Integer.MAX_VALUE)); } if (len > Integer.MAX_VALUE) { throw new UnsupportedOperationException( MessageFormat.format("Unable to extract a byte value larger than {0} bytes", Integer.MAX_VALUE)); } final ByteBuffer buf = struct.getVariableData(); if(preLen > buf.limit()) { return new byte[0]; } buf.position((int) Math.max(0, preLen)); final byte[] result = new byte[(int) len]; buf.get(result); return result; } /** * Extracts a signed int array from a structure. * * @param struct the structure to extract from * @param preLen the number of bytes before the value to extract * @param len the size of the stored value * @return the extracted int array * @since 1.1.2 */ public static int[] extractIntArray(final ResizableMemoryStructure struct, final long preLen, final long len) { if (len == 0) { return new int[0]; } if (preLen > Integer.MAX_VALUE) { throw new UnsupportedOperationException( MessageFormat.format("Unable to extract a int value with offset larger than {0} bytes", Integer.MAX_VALUE)); } if (len > Integer.MAX_VALUE) { throw new UnsupportedOperationException( MessageFormat.format("Unable to extract a int value larger than {0} bytes", Integer.MAX_VALUE)); } final ByteBuffer buf = struct.getVariableData(); if(preLen > buf.limit()) { return new int[0]; } buf.position((int) Math.max(0, preLen)); final int[] result = new int[(int) len]; for(int i = 0; i < len; i++) { result[i] = buf.getInt(); } return result; } /** * Extracts an array of variable-sized structures from the variable-data portion of a structure. * * @param the type of structure to extract * @param struct the parent structure * @param preLen the number of bytes before the value to extract * @param type a {@link Class} representing {@code } * @param count the count of structures to extract * @param sizeFunc a {@link Function} that determines the total length of each structure * @return a {@link List} of {@code T} objects * @since 1.0.44 */ public static List extractResizableStructures(ResizableMemoryStructure struct, int preLen, Class type, int count, Function sizeFunc) { ByteBuffer data = struct.getVariableData(); data.position(Math.max(0, preLen)); MemoryStructureWrapperService svc = MemoryStructureWrapperService.get(); List result = new ArrayList<>(count); int baseSize = svc.sizeOf(type); for(int i = 0; i < count; i++) { ByteBuffer sub = data.slice(); sub.limit(baseSize); T base = svc.wrapStructure(type, sub); int size = sizeFunc.apply(base); sub = data.slice(); sub.limit(size); result.add(svc.wrapStructure(type, sub)); data.position(data.position()+size); } return result; } public static T writeCompiledFormula(final T struct, final int preLen, final int currentLen, final String formula, final IntConsumer sizeWriter) { return StructureSupport.writeCompiledFormula(struct, preLen, currentLen, formula, (final long newLen) -> { if (sizeWriter!=null) { sizeWriter.accept((int) newLen); } }); } public static T writeCompiledFormula(final T struct, final long preLen, final long currentLen, final String formula, final LongConsumer sizeWriter) { ByteBuffer buf = struct.getVariableData(); final long otherLen = buf.remaining() - preLen - currentLen; if (preLen + currentLen > Integer.MAX_VALUE) { throw new UnsupportedOperationException( MessageFormat.format("Unable to write a new value with offset larger than {0} bytes", Integer.MAX_VALUE)); } if (otherLen > Integer.MAX_VALUE) { throw new UnsupportedOperationException( MessageFormat.format("Unable to write a new value with remaining offset larger than {0} bytes", Integer.MAX_VALUE)); } buf.position((int) (Math.max(0, preLen) + currentLen)); final byte[] otherData = new byte[(int) otherLen]; buf.get(otherData); final byte[] compiled = FormulaCompiler.get().compile(formula); if (sizeWriter != null) { sizeWriter.accept(compiled.length); } final long newLen = preLen + otherLen + compiled.length; if (newLen > Integer.MAX_VALUE) { throw new UnsupportedOperationException( MessageFormat.format("Unable to write a new value exceeding {0} bytes", Integer.MAX_VALUE)); } struct.resizeVariableData((int) newLen); buf = struct.getVariableData(); buf.position((int) preLen); buf.put(compiled); buf.put(otherData); return struct; } public static T writeStringValue(final T struct, final int preLen, final int currentLen, final String value, final IntConsumer sizeWriter) { return StructureSupport.writeStringValue(struct, preLen, currentLen, value, (final long newLen) -> sizeWriter.accept((int) newLen)); } public static T writeStringValue(final T struct, final int preLen, final int currentLen, final String value, boolean replaceLinebreaks, final IntConsumer sizeWriter) { return StructureSupport.writeStringValue(struct, preLen, currentLen, value, replaceLinebreaks, (final long newLen) -> sizeWriter.accept((int) newLen)); } public static T writeStringValue(final T struct, final long preLen, final long currentLen, final String value, final LongConsumer sizeWriter) { return writeStringValue(struct, preLen, currentLen, value, true, sizeWriter); } public static T writeStringValue(final T struct, final long preLen, final long currentLen, final String value, final boolean replaceLinebreaks, final LongConsumer sizeWriter) { ByteBuffer buf = struct.getVariableData(); final long otherLen = buf.remaining() - preLen - currentLen; if (preLen + currentLen > Integer.MAX_VALUE) { throw new UnsupportedOperationException( MessageFormat.format("Unable to write a new value with offset larger than {0} bytes", Integer.MAX_VALUE)); } if (otherLen > Integer.MAX_VALUE) { throw new UnsupportedOperationException( MessageFormat.format("Unable to write a new value with remaining offset larger than {0} bytes", Integer.MAX_VALUE)); } buf.position((int) (Math.max(0, preLen) + currentLen)); final byte[] otherData = new byte[(int) otherLen]; buf.get(otherData); final byte[] lmbcs; if (value == null) { lmbcs = new byte[0]; } else if (replaceLinebreaks) { lmbcs = value.getBytes(NativeItemCoder.get().getLmbcsCharset()); } else { lmbcs = value.getBytes(NativeItemCoder.get().getLmbcsCharset(LmbcsVariant.KEEPNEWLINES)); } if (sizeWriter != null) { sizeWriter.accept(lmbcs.length); } final long newLen = preLen + otherLen + lmbcs.length; if (newLen > Integer.MAX_VALUE) { throw new UnsupportedOperationException( MessageFormat.format("Unable to write a new value exceeding {0} bytes", Integer.MAX_VALUE)); } struct.resizeVariableData((int) newLen); buf = struct.getVariableData(); buf.position((int) preLen); buf.put(lmbcs); buf.put(otherData); return struct; } /** * Writes a list of string values as packed string into variable data. * * @param the type of structure containing the data * @param struct the structure to write to * @param preLen the number of bytes before the value to write * @param currentLengths the current length values of existing data * @param value the values to write * @param sizeWriter a callback to call with the new lengths * @return the value passed as {@code struct} * @since 1.0.38 */ public static T writeStringValues(final T struct, final int preLen, final long[] currentLengths, final List value, final Consumer sizeWriter) { if(currentLengths.length == 0) { return struct; } if(Objects.requireNonNull(currentLengths).length != Objects.requireNonNull(value).size()) { throw new IllegalArgumentException("The lengths of currentLengths and value must match"); } byte[][] data = new byte[value.size()][]; for(int i = 0; i < currentLengths.length; i++) { String val = value.get(i); if(val == null) { val = ""; //$NON-NLS-1$ } data[i] = val.getBytes(NativeItemCoder.get().getLmbcsCharset()); } ByteBuffer buf = struct.getVariableData(); long currentLen = LongStream.of(currentLengths).sum(); final long otherLen = buf.remaining() - preLen - currentLen; buf.position((int)(Math.max(0, preLen) + currentLen)); final byte[] otherData = new byte[(int) otherLen]; buf.get(otherData); long[] newLengths = Stream.of(data).mapToLong(d -> Integer.toUnsignedLong(d.length)).toArray(); long lmbcsLen = LongStream.of(newLengths).sum(); final long newLen = preLen + otherLen + lmbcsLen; if (newLen > Integer.MAX_VALUE) { throw new UnsupportedOperationException( MessageFormat.format("Unable to write a new value exceeding {0} bytes", Integer.MAX_VALUE)); } struct.resizeVariableData((int) newLen); buf = struct.getVariableData(); buf.position(preLen); for(byte[] lmbcs : data) { buf.put(lmbcs); } buf.put(otherData); if(sizeWriter != null) { sizeWriter.accept(newLengths); } return struct; } public static T writeStringValueShort(final T struct, final int preLen, final int currentLen, final String value, final Consumer sizeWriter) { return StructureSupport.writeStringValue(struct, preLen, currentLen, value, (final long newLen) -> sizeWriter.accept((short) newLen)); } /** * Writes an "unpacked" string value to the provided structure, using the * provided {@link IntConsumer} to update * an associated length value. For this use, "unpacked" means that: *
    *
  • The string is null-terminated
  • *
  • If the length of the string plus its terminator is odd, there is another * packing null * value at the end
  • *
* * @param the class of the structure * @param struct the structure to modify * @param preLen the amount of variable data in bytes before the place to * write * @param currentLen the current length of the variable data to overwrite * @param value the value to write * @param sizeWriter a callback to modify an associated length field, if * applicable * @return the provided structure */ public static T writeStringValueUnpacked(final T struct, final int preLen, final int currentLen, final String value, final IntConsumer sizeWriter) { ByteBuffer buf = struct.getVariableData(); final long otherLen = buf.remaining() - Math.max(0, preLen) - currentLen; if (Math.max(0, preLen) + currentLen > Integer.MAX_VALUE) { throw new UnsupportedOperationException( MessageFormat.format("Unable to write a new value with offset larger than {0} bytes", Integer.MAX_VALUE)); } if (otherLen > Integer.MAX_VALUE) { throw new UnsupportedOperationException( MessageFormat.format("Unable to write a new value with remaining offset larger than {0} bytes", Integer.MAX_VALUE)); } buf.position(Math.max(0, preLen) + currentLen); final byte[] otherData = new byte[(int) otherLen]; buf.get(otherData); final byte[] lmbcs = value == null ? new byte[0] : value.getBytes(NativeItemCoder.get().getLmbcsCharset()); /* Pad for a \0 terminator and for if the result will then be odd-numbered */ final int padLen = 1 + (lmbcs.length % 2 == 0 ? 1 : 0); if (sizeWriter != null) { sizeWriter.accept(lmbcs.length + padLen); } final long newLen = Math.max(0, preLen) + otherLen + lmbcs.length + padLen; if (newLen > Integer.MAX_VALUE) { throw new UnsupportedOperationException( MessageFormat.format("Unable to write a new value exceeding {0} bytes", Integer.MAX_VALUE)); } struct.resizeVariableData((int) newLen); buf = struct.getVariableData(); buf.position(Math.max(0, preLen)); buf.put(lmbcs); for (int i = 0; i < padLen; i++) { buf.put((byte) 0); } buf.put(otherData); return struct; } /** * Writes a "padded" string value to the provided structure, using the provided * {@link IntConsumer} to update * an associated length value. For this use, "unpacked" means that: *
    *
  • The string is not null-terminated
  • *
  • If the length of the string plus its terminator is odd, there is another * packing null * value at the end
  • *
* * @param the class of the structure * @param struct the structure to modify * @param preLen the amount of variable data in bytes before the place to * write * @param currentLen the current length of the variable data to overwrite * @param value the value to write * @param sizeWriter a callback to modify an associated length field, if * applicable * @return the provided structure */ public static T writeStringValueWordPadded(final T struct, final int preLen, final int currentLen, final String value, final IntConsumer sizeWriter) { ByteBuffer buf = struct.getVariableData(); final long otherLen = buf.remaining() - preLen - currentLen; if (Math.max(0, preLen) + currentLen > Integer.MAX_VALUE) { throw new UnsupportedOperationException( MessageFormat.format("Unable to write a new value with offset larger than {0} bytes", Integer.MAX_VALUE)); } if (otherLen > Integer.MAX_VALUE) { throw new UnsupportedOperationException( MessageFormat.format("Unable to write a new value with remaining offset larger than {0} bytes", Integer.MAX_VALUE)); } buf.position(Math.max(0, preLen) + currentLen); final byte[] otherData = new byte[(int) otherLen]; buf.get(otherData); final byte[] lmbcs = value == null ? new byte[0] : value.getBytes(NativeItemCoder.get().getLmbcsCharset()); /* Pad for word boundaries */ final int padLen = lmbcs.length % 2 == 0 ? 1 : 0; if (sizeWriter != null) { sizeWriter.accept(lmbcs.length + padLen); } final long newLen = preLen + otherLen + lmbcs.length + padLen; if (newLen > Integer.MAX_VALUE) { throw new UnsupportedOperationException( MessageFormat.format("Unable to write a new value exceeding {0} bytes", Integer.MAX_VALUE)); } struct.resizeVariableData((int) newLen); buf = struct.getVariableData(); buf.position(preLen); buf.put(lmbcs); for (int i = 0; i < padLen; i++) { buf.put((byte) 0); } buf.put(otherData); return struct; } /** * Writes a {@link ListStructure} value of null-delimited strings, using the provided * {@link IntConsumer} to update an associated length value. * * @param the class of the structure * @param struct the structure to modify * @param preLen the amount of variable data in bytes before the place to * write * @param currentLen the current length of the variable data to overwrite * @param value the value to write * @param sizeWriter a callback to modify an associated length field, if * applicable * @return the provided structure * @since 1.0.38 */ public static T writeStringListStructure(final T struct, final int preLen, final int currentLen, final List value, final IntConsumer sizeWriter) { ByteBuffer buf = struct.getVariableData(); final long otherLen = buf.remaining() - Math.max(0, preLen) - currentLen; if (Math.max(0, preLen) + currentLen > Integer.MAX_VALUE) { throw new UnsupportedOperationException( MessageFormat.format("Unable to write a new value with offset larger than {0} bytes", Integer.MAX_VALUE)); } if (otherLen > Integer.MAX_VALUE) { throw new UnsupportedOperationException( MessageFormat.format("Unable to write a new value with remaining offset larger than {0} bytes", Integer.MAX_VALUE)); } buf.position(Math.max(0, preLen) + currentLen); final byte[] otherData = new byte[(int) otherLen]; buf.get(otherData); byte[] lmbcs; try(ByteArrayOutputStream baos = new ByteArrayOutputStream()) { for(int i = 0; i < value.size(); i++) { String val = value.get(i); byte[] data = val == null ? new byte[0] : val.getBytes(NativeItemCoder.get().getLmbcsCharset()); baos.write(data); baos.write(0); } lmbcs = baos.toByteArray(); } catch(IOException e) { throw new UncheckedIOException(e); } final long newLen = Math.max(0, preLen) + otherLen + 2 + lmbcs.length; if (newLen > Integer.MAX_VALUE) { throw new UnsupportedOperationException( MessageFormat.format("Unable to write a new value exceeding {0} bytes", Integer.MAX_VALUE)); } struct.resizeVariableData((int) newLen); buf = struct.getVariableData(); buf.position(Math.max(0, preLen)); buf.putShort((short)value.size()); buf.put(lmbcs); buf.put(otherData); if(sizeWriter != null) { sizeWriter.accept(lmbcs.length + 2); } return struct; } /** * Writes a raw byte value into the variable-data portion of a structure, using the provided * {@link IntConsumer} to update an associated length value. * * @param the class of the structure * @param struct the structure to modify * @param preLen the amount of variable data in bytes before the place to * write * @param currentLen the current length of the variable data to overwrite * @param valueParam the value to write * @param sizeWriter a callback to modify an associated length field, if * applicable * @return the provided structure * @since 1.0.43 */ public static T writeByteValue(final T struct, final long preLen, final long currentLen, final byte[] valueParam, final IntConsumer sizeWriter) { byte[] value = valueParam == null ? new byte[0] : valueParam; ByteBuffer buf = struct.getVariableData(); final long otherLen = buf.remaining() - Math.max(0, preLen) - currentLen; if (Math.max(0, preLen) + currentLen > Integer.MAX_VALUE) { throw new UnsupportedOperationException( MessageFormat.format("Unable to write a new value with offset larger than {0} bytes", Integer.MAX_VALUE)); } if (otherLen > Integer.MAX_VALUE) { throw new UnsupportedOperationException( MessageFormat.format("Unable to write a new value with remaining offset larger than {0} bytes", Integer.MAX_VALUE)); } buf.position((int) (Math.max(0, preLen) + currentLen)); final byte[] otherData = new byte[(int) otherLen]; buf.get(otherData); if (sizeWriter != null) { sizeWriter.accept(value.length); } final long newLen = Math.max(0, preLen) + otherLen + value.length; if (newLen > Integer.MAX_VALUE) { throw new UnsupportedOperationException( MessageFormat.format("Unable to write a new value exceeding {0} bytes", Integer.MAX_VALUE)); } struct.resizeVariableData((int) newLen); buf = struct.getVariableData(); buf.position((int) Math.max(0, preLen)); buf.put(value); buf.put(otherData); return struct; } /** * Writes an int array value into the variable-data portion of a structure, using the provided * {@link IntConsumer} to update an associated length value. * * @param the class of the structure * @param struct the structure to modify * @param preLen the amount of variable data in bytes before the place to * write * @param currentLen the current length of the variable data to overwrite * @param valueParam the value to write * @param sizeWriter a callback to modify an associated length field, if * applicable * @return the provided structure * @since 1.1.2 */ public static T writeIntValue(final T struct, final long preLen, final long currentLen, final int[] valueParam, final IntConsumer sizeWriter) { int[] value = valueParam == null ? new int[0] : valueParam; ByteBuffer buf = struct.getVariableData(); final long otherLen = buf.remaining() - Math.max(0, preLen) - currentLen; if (Math.max(0, preLen) + currentLen > Integer.MAX_VALUE) { throw new UnsupportedOperationException( MessageFormat.format("Unable to write a new value with offset larger than {0} bytes", Integer.MAX_VALUE)); } if (otherLen > Integer.MAX_VALUE) { throw new UnsupportedOperationException( MessageFormat.format("Unable to write a new value with remaining offset larger than {0} bytes", Integer.MAX_VALUE)); } buf.position((int) (Math.max(0, preLen) + currentLen)); final byte[] otherData = new byte[(int) otherLen]; buf.get(otherData); int valueLen = value.length * 4; if (sizeWriter != null) { sizeWriter.accept(valueLen); } final long newLen = Math.max(0, preLen) + otherLen + valueLen; if (newLen > Integer.MAX_VALUE) { throw new UnsupportedOperationException( MessageFormat.format("Unable to write a new value exceeding {0} bytes", Integer.MAX_VALUE)); } struct.resizeVariableData((int) newLen); buf = struct.getVariableData(); buf.position((int)Math.max(0, preLen)); Arrays.stream(value).forEach(buf::putInt); buf.put(otherData); return struct; } /** * Reads an LMBCS string from a byte array, such as those of fixed-size struct members. * *

This method handles reading only up to the first null value.

* * @param lmbcs the fixed-size array containing the string * @return the read value * @since 1.0.34 */ public static String readLmbcsValue(byte[] lmbcs) { int firstNull = 0; for(firstNull = 0; firstNull < lmbcs.length; firstNull++) { if(lmbcs[firstNull] == '\0') { break; } } return new String(lmbcs, 0, firstNull, NativeItemCoder.get().getLmbcsCharset()); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy