ByteBucket.java
/*
** Module : ByteBucket.java
** Abstract : Implementation of the OpenEdge.Core.ByteBucket builtin class.
**
** Copyright (c) 2019-2023, Golden Code Development Corporation.
**
** -#- -I- --Date-- ---------------------------------------Description----------------------------------------
** 001 CA 20190526 First version, stubs taken by converting the skeleton using FWD.
** 002 CA 20191024 Added method support levels and updated the class support level.
** 003 CA 20200313 Fixed a typo in putBytes_1 LegacySignature annotation.
** 004 GES 20200429 Shifted to new legacy enum approach.
** 005 ME 20200604 Rewrite to match the 4GL implementation.
** 20201019 Always write at the end regardless of position set, check max size
** boundary (2G).
** 006 CA 20210113 Renamed clear() to clear_(), to follow NameConverter's rules.
** 007 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy
** signature.
** ME 20210310 Use object variables defined out of block for methods that returns objects.
** ME 20210325 Move assert inside block else initInput returns an invalid `object`.
** ME 20210915 Fix NPE with @longValue@ on size, throw error #4391 for out of range position.
** VVT 20210917 Javadoc fixed.
** ME 20210929 Fix procedure blocks for some methods.
** CA 20220923 Variable definitions (including associated with parameters) must be done always outside of
** the BlockManager API
** 008 CA 20231113 The 'execute' method must be annotated with LegacySignature Type.Execute, and also can be
** dropped if is a no-op.
** 009 AL2 20250530 Fixed putBytes to account for cases where there is no remainder after batching.
*/
/*
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU Affero General Public License as
** published by the Free Software Foundation, either version 3 of the
** License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU Affero General Public License for more details.
**
** You may find a copy of the GNU Affero GPL version 3 at the following
** location: https://www.gnu.org/licenses/agpl-3.0.en.html
**
** Additional terms under GNU Affero GPL version 3 section 7:
**
** Under Section 7 of the GNU Affero GPL version 3, the following additional
** terms apply to the works covered under the License. These additional terms
** are non-permissive additional terms allowed under Section 7 of the GNU
** Affero GPL version 3 and may not be removed by you.
**
** 0. Attribution Requirement.
**
** You must preserve all legal notices or author attributions in the covered
** work or Appropriate Legal Notices displayed by works containing the covered
** work. You may not remove from the covered work any author or developer
** credit already included within the covered work.
**
** 1. No License To Use Trademarks.
**
** This license does not grant any license or rights to use the trademarks
** Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
** of Golden Code Development Corporation. You are not authorized to use the
** name Golden Code, FWD, or the names of any author or contributor, for
** publicity purposes without written authorization.
**
** 2. No Misrepresentation of Affiliation.
**
** You may not represent yourself as Golden Code Development Corporation or FWD.
**
** You may not represent yourself for publicity purposes as associated with
** Golden Code Development Corporation, FWD, or any author or contributor to
** the covered work, without written authorization.
**
** 3. No Misrepresentation of Source or Origin.
**
** You may not represent the covered work as solely your work. All modified
** versions of the covered work must be marked in a reasonable way to make it
** clear that the modified work is not originating from Golden Code Development
** Corporation or FWD. All modified versions must contain the notices of
** attribution required in this license.
*/
package com.goldencode.p2j.oo.core;
import com.goldencode.p2j.util.*;
import com.goldencode.p2j.util.BlockManager.Action;
import com.goldencode.p2j.util.BlockManager.Condition;
import com.goldencode.p2j.oo.lang.*;
import static com.goldencode.p2j.util.BlockManager.*;
import static com.goldencode.p2j.report.ReportConstants.*;
import static com.goldencode.p2j.util.InternalEntry.Type;
import java.util.*;
/**
* Business logic (converted to Java from the 4GL source code
* in OpenEdge/Core/ByteBucket.cls).
*/
@LegacyResource(resource = "OpenEdge.Core.ByteBucket")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_PARTIAL)
public class ByteBucket extends BaseObject implements ISupportInitialize
{
/** Bucket row size */
private static final int MAX_BYTES_PER_ROW = 0x7800;
/** The initial size of the memptrs that are held in the Bucket's array.
* Not used in the current implementation
*/
private int64 defaultCapacity = TypeFactory.int64();
/** The current position */
protected int64 position = TypeFactory.int64(0L);
/** The current write position, putBytes always seems to write at the end regardless of position set **/
private long writePosition = 0L;
/** The bucket size (total number of bytes) */
protected int64 size = TypeFactory.int64(0L);
/** List of rows */
protected LinkedList<byte[]> buckets = new LinkedList<>();
/**
* Execute method
*/
@LegacySignature(type = Type.EXECUTE)
public void __core_ByteBucket_execute__()
{
onBlockLevel(Condition.ERROR, Action.THROW);
}
/**
* Get the initial size of the memptrs that are held in the Bucket's array.
* Each memptr is the same size. Not used in the current implementation.
*
* @return the initial size of the memptrs that are held in the Bucket's array.
*/
@LegacySignature(returns = "INT64", type = Type.GETTER, name = "DefaultCapacity")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public int64 getDefaultCapacity()
{
return function(this, "DefaultCapacity", int64.class, new Block((Body) () -> {
returnNormal(defaultCapacity);
}));
}
/**
* Get the current read position.
*
* @return the current read position.
*/
@LegacySignature(returns = "INT64", type = Type.GETTER, name = "Position")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public int64 getPosition()
{
return function(this, "Position", int64.class, new Block((Body) () -> {
returnNormal(position);
}));
}
/**
* Set the current read position.
*
* @param _var the new current read position.
*/
@LegacySignature(type = Type.SETTER, name = "Position", parameters = {
@LegacyParameter(name = "var", type = "INT64", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public void setPosition(final int64 _var)
{
int64 var = TypeFactory.initInput(_var);
internalProcedure(ByteBucket.class, this, "Position", new Block((Body) () -> {
position.assign(var);
}));
}
/**
* Returns the size of the data in the bucket.
* @return the size of the data in the bucket.
*/
@LegacySignature(returns = "INT64", type = Type.GETTER, name = "Size")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public int64 getSize()
{
return function(this, "Size", int64.class, new Block((Body) () -> {
// make sure size is not unknown (destroy), $GL BUG probably not checking for valid object
if (size.isUnknown())
{
ErrorManager.recordOrThrowError(3135, "Invalid handle. Not initialized or points to a deleted object");
}
else
{
returnNormal(size);
}
}));
}
/**
* Constructor.
*/
@LegacySignature(type = Type.CONSTRUCTOR)
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public static void __core_ByteBucket_constructor__static__()
{
externalProcedure(ByteBucket.class, new Block((Body) () -> {
onBlockLevel(Condition.ERROR, Action.THROW);
}));
}
/**
* Constructor.
*/
@LegacySignature(type = Type.CONSTRUCTOR)
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public void __core_ByteBucket_constructor__()
{
internalProcedure(ByteBucket.class, this, "__core_ByteBucket_constructor__", new Block((Body) () -> {
__core_ByteBucket_constructor__(new int64(0));
}));
}
/**
* Constructor.
*
* @param _defaultCapacity
* the default capacity of the memptrs that are held in the Bucket's array.
*/
@LegacySignature(type = Type.CONSTRUCTOR, parameters = {
@LegacyParameter(name = "defaultCapacity", type = "INT64", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public void __core_ByteBucket_constructor__(final int64 _defaultCapacity)
{
int64 defaultCapacity = TypeFactory.initInput(_defaultCapacity);
internalProcedure(ByteBucket.class, this, "__core_ByteBucket_constructor__", new Block((Body) () -> {
__lang_BaseObject_constructor__();
// just throw a 'matching' error for now until implementation is using the new MemoryOutputStream
if (defaultCapacity.isUnknown() || defaultCapacity.longValue() < 0) {
ErrorManager.recordOrThrowError(18193, "Invalid value specified for parameter 'value' of method or constructor 'Progress.IO.MemoryOutputStream'.");
}
if (defaultCapacity.longValue() != 0 ) {
if (defaultCapacity.longValue() > Integer.MAX_VALUE ) {
ErrorManager.recordOrThrowError(0, "The initial stream size cannot be greater than 2GB");
}
// pre-allocate memory???
for (int i = 0; i < (int) (defaultCapacity.longValue() / MAX_BYTES_PER_ROW); i++)
{
buckets.add(new byte[MAX_BYTES_PER_ROW]);
}
}
this.defaultCapacity.assign(defaultCapacity);
this.position.assign(1);
}));
}
/**
* Constructor.
*
* @param _initialSize
* the initial size of the memptrs that are held in the Bucket's array.
*/
@LegacySignature(type = Type.CONSTRUCTOR, parameters = {
@LegacyParameter(name = "initialSize", type = "INTEGER", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public void __core_ByteBucket_constructor__(final integer _initialSize)
{
integer initialSize = TypeFactory.initInput(_initialSize);
internalProcedure(ByteBucket.class, this, "__core_ByteBucket_constructor__", new Block((Body) () -> {
__core_ByteBucket_constructor__();
Assert.isPositive(initialSize, new character("Initial size"));
}));
}
/**
* Constructor.
*
* @param initialSize
* the initial size of the memptrs that are held in the Bucket's array.
* @param defaultCapacity
* the initial number of buckets (not used in the current implementation).
*/
@LegacySignature(type = Type.CONSTRUCTOR, parameters = {
@LegacyParameter(name = "initialSize", type = "INTEGER", mode = "INPUT"),
@LegacyParameter(name = "defaultCapacity", type = "INT64", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public void __core_ByteBucket_constructor__(final integer initialSize, final int64 defaultCapacity)
{
// default capacity input parameter ignored on 4GL, deprecated since 11.7.0
// in OE12.2 initial size (number of rows) is not used anymore but default capacity (bytes) is used instead
__core_ByteBucket_constructor__(defaultCapacity);
}
/**
* Destructor for this class. Cleanup/delete any created resources.
*/
@LegacySignature(type = Type.DESTRUCTOR)
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public void __core_ByteBucket_destructor__()
{
internalProcedure(ByteBucket.class, this, "__core_ByteBucket_destructor__", new Block((Body) () -> {
destroy();
}));
}
/**
* Clears/resets the ByteBucket. Does not de-allocate the memory,
* just the various pointers/counters/cursors.
*/
@LegacySignature(type = Type.METHOD, name = "Clear")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public void clear_()
{
internalProcedure(ByteBucket.class, this, "Clear", new Block((Body) () -> {
this.position.assign(1);
this.size.assign(0);
}));
}
/**
* Debug method to dump out current RAW bytes into a file.
* The file is saved in session temporary folder as "bytebucket-memptr-<object_id>.bin"
*/
@LegacySignature(type = Type.METHOD, name = "Debug")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public void debug()
{
internalProcedure(ByteBucket.class, this, "Debug", new Block((Body) () -> {
String debugPath = String.format("%sbytebucket-memptr-%d.bin",
EnvironmentOps.getTempDirectory().getValue(),
handle.resourceId(ObjectOps.asResource(this)));
memptr ptr = getBytes().ref().getValue();
try
{
new LobCopy(new SourceLob(ptr), new TargetLobFile(debugPath)).run();
}
finally
{
ptr.setLength(0);
}
}));
}
/**
* Destroy.
*/
@Override
@LegacySignature(type = Type.METHOD, name = "Destroy")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public void destroy()
{
internalProcedure(ByteBucket.class, this, "Destroy", new Block((Body) () -> {
this.position.assign(1);
this.size.setUnknown();
buckets.clear();
}));
}
/**
* Returns a byte at the current position, and increments the position marker.
* <b>when position is out of range method should return unknown instead of 0</b>
*
* @return the byte at the current position.
*/
@LegacySignature(returns = "INTEGER", type = Type.METHOD, name = "GetByte")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public integer getByte()
{
return function(this, "GetByte", integer.class, new Block((Body) () -> {
integer iByte = getByte(position);
this.position.assign(new int64(this.position.longValue() + 1));
returnNormal(iByte);
}));
}
/**
* Returns a byte at the specified position. Contrary to the 4DL documentation,
* <b>does not increment the position marker</b>
* <b>when specified position is out of range method should return unknown instead of 0</b>
*
* @param _pos the position at which to return the byte.
* @return the byte value at the position.
*/
@LegacySignature(returns = "INTEGER", type = Type.METHOD, name = "GetByte", parameters = {
@LegacyParameter(name = "pos", type = "INT64", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public integer getByte(final int64 _pos)
{
int64 p = TypeFactory.initInput(_pos);
return function(this, "GetByte", integer.class, new Block((Body) () -> {
if (!p.isUnknown()) {
long pos = p.longValue();
if (pos < 1)
{
BinaryData.genIndexError();
}
else if (pos <= getSize().longValue())
{
int nbucket = (int) ((pos - 1) / MAX_BYTES_PER_ROW);
int offset = (int) ((pos - 1) % MAX_BYTES_PER_ROW);
if (nbucket < buckets.size())
{
byte[] bucket = buckets.get(nbucket);
returnNormal(new integer(bucket[offset] & 0xFF));
}
}
}
returnNormal(new integer(0));
}));
}
/**
* Returns the entire contents of this bucket as a Memptr instance.
*
* @return the complete bucket data as a Memptr instance.
*/
@LegacySignature(returns = "OBJECT", type = Type.METHOD, name = "GetBytes", qualified = "openedge.core.memptr")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public object<? extends Memptr> getBytes()
{
return getBytes(new int64(1), this.size);
}
/**
* Returns a Memptr instance containing the specified number of bytes, starting at the
* current position.
*
* @param size the number of bytes to return.
*
* @return the bucket data.
*/
@LegacySignature(returns = "OBJECT", type = Type.METHOD, name = "GetBytes", qualified = "openedge.core.memptr", parameters = {
@LegacyParameter(name = "size", type = "INT64", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public object<? extends Memptr> getBytes(final int64 size)
{
return getBytes(this.position, size);
}
/**
* Returns a Memptr instance containing the specified number of bytes, starting at the
* specified position.
*
* @param _pos the starting position.
* @param _size the number of bytes to return.
*
* @return the bucket data.
*/
@LegacySignature(returns = "OBJECT", type = Type.METHOD, name = "GetBytes", qualified = "openedge.core.memptr", parameters = {
@LegacyParameter(name = "pos", type = "INT64", mode = "INPUT"),
@LegacyParameter(name = "size", type = "INT64", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public object<? extends Memptr> getBytes(final int64 _pos, final int64 _size)
{
int64 pos = TypeFactory.initInput(_pos);
int64 size = TypeFactory.initInput(_size);
object<? extends Memptr> ret = TypeFactory.object(Memptr.class);
memptr ptr = TypeFactory.memptr();
return function(this, "GetBytes", object.class, new Block((Body) () -> {
if (getSize().longValue() == 0 || size.getValue() == 0)
{
ret.assign(Memptr.getEmpty());
}
else
{
try
{
ptr.setLength(size);
readBytes(pos, ptr);
ret.assign(ObjectOps.newInstance(Memptr.class, "I", ptr));
}
finally
{
ptr.setLength(0L);
}
}
returnNormal(ret);
}));
}
/**
* Returns a hash of the current contents of the bucket.
*
* @return the hashed value of the bucket.
*/
@LegacySignature(returns = "RAW", type = Type.METHOD, name = "GetHash")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public raw getHash()
{
return getHash(HashAlgorithmEnum.md5);
}
/**
* Returns a hash of the current contents of the bucket.
*
* @param _p1 - the algorithm to use.
*
* @return the hashed value of the bucket.
*/
@LegacySignature(returns = "RAW", type = Type.METHOD, name = "GetHash", parameters = {
@LegacyParameter(name = "p1", type = "OBJECT", qualified = "openedge.core.hashalgorithmenum", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public raw getHash(final object<? extends HashAlgorithmEnum> _p1)
{
object<? extends HashAlgorithmEnum> p1 = TypeFactory.initInput(_p1);
return function(this, "GetHash", raw.class, new Block((Body) () -> {
Assert.notNull(_p1, new character("Algorithm"));
returnNormal(getBytes().ref().getHash(p1));
}));
}
/**
* Returns a string/character representation of the entire set of bytes.
*
* @return the character/string data requested.
*/
@LegacySignature(returns = "LONGCHAR", type = Type.METHOD, name = "GetString")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public longchar getString()
{
return getString(new int64(1), this.getSize());
}
/**
* Returns a string/character representation a particular number of bytes, from the current
* position.
*
* @param size the size of the data (in bytes) to return.
*
* @return the character/string data requested.
*/
@LegacySignature(returns = "LONGCHAR", type = Type.METHOD, name = "GetString", parameters = {
@LegacyParameter(name = "size", type = "INT64", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public longchar getString(final int64 size)
{
return getString(this.position, size);
}
/**
* Returns a string/character representation a particular number of bytes, from a given start
* position.
*
* @param pos
* the start position.
* @param size
* the size of the data (in bytes) to return.
*
* @return the character/string data requested.
*/
@LegacySignature(returns = "LONGCHAR", type = Type.METHOD, name = "GetString", parameters = {
@LegacyParameter(name = "pos", type = "INT64", mode = "INPUT"),
@LegacyParameter(name = "size", type = "INT64", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public longchar getString(final int64 pos, final int64 size)
{
return getString(pos, size, I18nOps.getCPInternal(), new character("UTF-8"));
}
/**
* Returns a string/character representation a particular number of bytes, from a given start
* position.
*
* @param pos
* the start position.
* @param size
* the size of the data (in bytes) to return.
* @param targetCP
* the target codepage for the character data.
*
* @return the character/string data requested.
*/
@LegacySignature(returns = "LONGCHAR", type = Type.METHOD, name = "GetString", parameters = {
@LegacyParameter(name = "pos", type = "INT64", mode = "INPUT"),
@LegacyParameter(name = "size", type = "INT64", mode = "INPUT"),
@LegacyParameter(name = "targetCP", type = "CHARACTER", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public longchar getString(final int64 pos, final int64 size, final character targetCP)
{
return getString(pos, size, I18nOps.getCPInternal(), targetCP);
}
/**
* Returns a string/character representation a particular number of bytes, from a given start
* position.
*
* @param _pos the start position.
* @param _size the size of the data (in bytes) to return.
* @param _sourceCP the source codepage for the character data.
* @param _targetCP the target codepage for the character data.
*
* @return the character/string data requested.
*/
@LegacySignature(returns = "LONGCHAR", type = Type.METHOD, name = "GetString", parameters = {
@LegacyParameter(name = "pos", type = "INT64", mode = "INPUT"),
@LegacyParameter(name = "size", type = "INT64", mode = "INPUT"),
@LegacyParameter(name = "sourceCP", type = "CHARACTER", mode = "INPUT"),
@LegacyParameter(name = "targetCP", type = "CHARACTER", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public longchar getString(final int64 _pos, final int64 _size, final character _sourceCP,
final character _targetCP)
{
int64 pos = TypeFactory.initInput(_pos);
int64 size = TypeFactory.initInput(_size);
character targetCP = TypeFactory.initInput(_targetCP);
character sourceCP = TypeFactory.initInput(_sourceCP);
longchar lc = TypeFactory.longchar();
memptr ptr = TypeFactory.memptr();
return function(this, "GetString", longchar.class, new Block((Body) () -> {
Assert.isPositive(pos, new character("Start position"));
Assert.isZeroOrPositive(size, new character("Slice size"));
if (TextOps.isEmpty(targetCP))
targetCP.assign("UTF-8");
lc.fixCodePage(targetCP);
if (CompareOps._isEqual(size, 0) || CompareOps._isEqual(getSize(), 0))
{
returnNormal(lc);
}
if (TextOps.isEmpty(sourceCP))
sourceCP.assign("UTF-8");
try
{
ptr.setLength(size);
readBytes(pos, ptr);
new LobCopy(new SourceLob(ptr), new TargetLob(lc)).sourceCodePage(sourceCP)
.targetCodePage(targetCP).run();
returnNormal(lc);
}
finally
{
ptr.setLength(0L);
}
}));
}
/**
* Initialize
*/
@Override
@LegacySignature(type = Type.METHOD, name = "Initialize")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public void initialize()
{
internalProcedure(ByteBucket.class, this, "Initialize", new Block((Body) () -> {
clear_();
}));
}
/**
* Factory method for creating a ByteBucket.
*
* @return a created bucket.
*/
@LegacySignature(returns = "OBJECT", type = Type.METHOD, name = "Instance", qualified = "openedge.core.bytebucket")
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public static object<? extends ByteBucket> instance()
{
object<? extends ByteBucket> bb = TypeFactory.object(ByteBucket.class);
return function(ByteBucket.class, "Instance", object.class, new Block((Body) () -> {
bb.assign(ObjectOps.newInstance(ByteBucket.class));
bb.ref().initialize();
returnNormal(bb);
}));
}
/**
* Factory method for creating a ByteBucket.
*
* @param _p1 the size of each memptr in the array (not used in the current implementation).
*
* @return a created bucket.
*/
@LegacySignature(returns = "OBJECT", type = Type.METHOD, name = "Instance", qualified = "openedge.core.bytebucket", parameters = {
@LegacyParameter(name = "p1", type = "INT64", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public static object<? extends ByteBucket> instance(final int64 _p1)
{
int64 p1 = TypeFactory.initInput(_p1);
object<? extends ByteBucket> bb = TypeFactory.object(ByteBucket.class);
return function(ByteBucket.class, "Instance", object.class, new Block((Body) () -> {
bb.assign(ObjectOps.newInstance(ByteBucket.class, "I", p1));
bb.ref().initialize();
returnNormal(bb);
}));
}
/**
* Factory method for creating a ByteBucket.
*
* @param _p1 the initial size of the array (not used in the current implementation).
* @param _p2 the size of each memptr in the array (not used in the current implementation).
*
* @return a created bucket.
*/
@LegacySignature(returns = "OBJECT", type = Type.METHOD, name = "Instance", qualified = "openedge.core.bytebucket", parameters = {
@LegacyParameter(name = "p1", type = "INTEGER", mode = "INPUT"),
@LegacyParameter(name = "p2", type = "INT64", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public static object<? extends ByteBucket> instance(final integer _p1, final int64 _p2)
{
integer p1 = TypeFactory.initInput(_p1);
int64 p2 = TypeFactory.initInput(_p2);
object<? extends ByteBucket> bb = TypeFactory.object(ByteBucket.class);
return function(ByteBucket.class, "Instance", object.class, new Block((Body) () -> {
bb.assign(ObjectOps.newInstance(ByteBucket.class, "II", p1, p2));
bb.ref().initialize();
returnNormal(bb);
}));
}
/**
* Copies all of the bytes from a ByteBucket instance into this bucket.
*
* @param _data the ByteBucket instance containing the data.
*/
@LegacySignature(type = Type.METHOD, name = "PutBytes", parameters = {
@LegacyParameter(name = "data", type = "OBJECT", qualified = "openedge.core.bytebucket", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public void putBytes(final object<? extends ByteBucket> _data)
{
object<? extends ByteBucket> data = TypeFactory.initInput(_data);
internalProcedure(ByteBucket.class, this, "PutBytes", new Block((Body) () -> {
Assert.notNull(data, new character("Input data"));
ByteBucket bbucket = data.ref();
long size = bbucket.getSize().longValue();
if (size > 0)
{
int endBucket = (int) (size / MAX_BYTES_PER_ROW);
int endOffset = (int) (size % MAX_BYTES_PER_ROW);
for (int i = 0; i < endBucket + (endOffset > 0 ? 1 : 0); i++)
{
byte[] bucket = bbucket.buckets.get(i);
writeBytes(bucket, i < endBucket ? MAX_BYTES_PER_ROW : endOffset);
}
}
}));
}
/**
* Copies all of the bytes from a memptr into this bucket.
*
* @param _ptr the pointer to memory represented by a memptr.
* @param _size the size of the memptr represented by the pointer value.
*/
@LegacySignature(type = Type.METHOD, name = "PutBytes", parameters = {
@LegacyParameter(name = "ptr", type = "INT64", mode = "INPUT"),
@LegacyParameter(name = "size", type = "INT64", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public void putBytes(final int64 _ptr, final int64 _size)
{
int64 ptr = TypeFactory.initInput(_ptr);
int64 size = TypeFactory.initInput(_size);
memptr mptr = TypeFactory.memptr();
internalProcedure(ByteBucket.class, this, "PutBytes", new Block((Body) () -> {
if (!size.isUnknown() && size.longValue() == 0L)
return;
Assert.isPositive(ptr, new character("Pointer Value"));
Assert.isPositive(size, new character("Memptr Size"));
try
{
mptr.setPointerValue(ptr.longValue());
mptr.setLength(size.longValue());
writeBytes(mptr);
}
finally
{
mptr.setPointerValue(0L);
if (mptr.lengthOf() > 0L)
mptr.setLength(0L);
}
}));
}
/**
* Copies all of the bytes from a memptr (primitive) into this bucket.
*
* @param _ptr the memptr containing the data.
*/
@LegacySignature(type = Type.METHOD, name = "PutBytes", parameters = {
@LegacyParameter(name = "ptr", type = "MEMPTR", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public void putBytes(final memptr _ptr)
{
memptr ptr = TypeFactory.initInput(_ptr);
internalProcedure(ByteBucket.class, this, "PutBytes", new Block((Body) () -> {
writeBytes(ptr);
}));
}
/**
* Copies all of the bytes from a Memptr instance into this bucket.
*
* @param _mptr the Memptr instance containing the data.
*/
@LegacySignature(type = Type.METHOD, name = "PutBytes", parameters = {
@LegacyParameter(name = "p1", type = "OBJECT", qualified = "OpenEdge.Core.Memptr", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public void putBytes_1(final object<? extends Memptr> _mptr)
{
object<? extends Memptr> mptr = TypeFactory.initInput(_mptr);
internalProcedure(ByteBucket.class, this, "PutBytes", new Block((Body) () -> {
Assert.notNull(_mptr, new character("Data"));
putBytes(mptr.ref().getPointerValue(), mptr.ref().getSize());
}));
}
/**
* Copies all of the bytes from a longchar into this bucket.
*
* @param _data the longchar containing the source data.
*/
@LegacySignature(type = Type.METHOD, name = "PutString", parameters = {
@LegacyParameter(name = "data", type = "LONGCHAR", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public void putString(final longchar _data)
{
longchar data = TypeFactory.initInput(_data);
internalProcedure(ByteBucket.class, this, "PutString", new Block((Body) () -> {
putString(data, new longchar("UTF-8"));
}));
}
/**
* Copies all of the bytes from a longchar into this bucket.
*
* @param data the longchar containing the source data.
* @param targetCP the target codepage used to write data into the bucket.
* Defaults to UTF-8.
*/
@LegacySignature(type = Type.METHOD, name = "PutString", parameters = {
@LegacyParameter(name = "data", type = "LONGCHAR", mode = "INPUT"),
@LegacyParameter(name = "targetCP", type = "LONGCHAR", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public void putString(final longchar data, final longchar targetCP)
{
putString(data, new character(targetCP));
}
/**
* Copies all of the bytes from a longchar into this bucket.
*
* @param _data the longchar containing the source data.
* @param _targetCP the target codepage used to write data into the bucket.
* Defaults to UTF-8.
*/
@LegacySignature(type = Type.METHOD, name = "PutString", parameters = {
@LegacyParameter(name = "data", type = "LONGCHAR", mode = "INPUT"),
@LegacyParameter(name = "targetCP", type = "CHARACTER", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public void putString(final longchar _data, final character _targetCP)
{
longchar data = TypeFactory.initInput(_data);
character targetCP = TypeFactory.initInput(_targetCP);
memptr ptr = TypeFactory.memptr();
internalProcedure(ByteBucket.class, this, "PutString", new Block((Body) () -> {
if (TextOps.isEmpty(data))
return;
if (TextOps.isEmpty(targetCP))
{
targetCP.assign("UTF-8");
}
try
{
new LobCopy(new SourceLob(data), new TargetLob(ptr)).targetCodePage(targetCP).run();
putBytes(ptr);
}
finally
{
ptr.setLength(0L);
}
}));
}
/**
* Copies all of the bytes from a String object into this bucket.
*
* @param _data the String containing the source data
*/
@LegacySignature(type = Type.METHOD, name = "PutString", parameters = {
@LegacyParameter(name = "data", type = "OBJECT", qualified = "openedge.core.string", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public void putString(final object<? extends LegacyString> _data)
{
object<? extends LegacyString> data = TypeFactory.initInput(_data);
internalProcedure(ByteBucket.class, this, "PutString", new Block((Body) () -> {
Assert.notNull(data, new character("String data"));
putString(data.ref().getValue(), new longchar(data.ref().getEncoding()));
}));
}
/**
* Resizes the internal 'array' of records. We can shrink down the number of rows, but not
* smaller than the bucket's size in bytes. Not used in the current implementation.
*
* @param _p1 the new size (number of records) for the internal structure.
*/
@LegacySignature(type = Type.METHOD, name = "Resize", parameters = {
@LegacyParameter(name = "p1", type = "INTEGER", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public void resize(final integer _p1)
{
// no-op in OE12.2
}
/**
* Resizes the internal 'array' of records. We can shrink down the number of rows, but not
* smaller than the bucket's size in bytes. Not used in the current implementation.
*
* @param _p1 The new size (number of extents) for the array.
*/
@LegacySignature(type = Type.METHOD, name = "ResizeArray", parameters = {
@LegacyParameter(name = "p1", type = "INTEGER", mode = "INPUT") })
@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
public void resizeArray(final integer _p1)
{
// no-op in oe12.2
}
/**
* Read data into the byte array.
*
* @param p
* start position.
* @param data
* data.
*/
private void readBytes(int64 p, memptr data)
{
Assert.isPositive(p, new character("Start position"));
if (CompareOps._isGreaterThan(p, this.getSize()))
{
ErrorManager.recordOrThrowError(4391,
"Unable to evaluate expression with UNKNOWN value in argument");
}
long pos = p.longValue();
long bytesLeft = data.lengthOf();
int putAt = 0;
int nbucket = (int) ((pos - 1) / MAX_BYTES_PER_ROW);
int offset = (int) ((pos - 1) % MAX_BYTES_PER_ROW);
int endBucket = (int) ((size.longValue()) / MAX_BYTES_PER_ROW);
int endOffset = (int) ((size.longValue()) % MAX_BYTES_PER_ROW);
for (int i = nbucket; i <= endBucket; i++)
{
byte[] bucket = buckets.get(i);
int numBytes = (int) Math.min((i == endBucket ? endOffset : MAX_BYTES_PER_ROW) - offset, bytesLeft);
if (numBytes > 0)
{
data.write(true, Arrays.copyOfRange(bucket, offset, offset + numBytes), putAt, false);
offset = 0;
putAt += numBytes;
bytesLeft -= numBytes;
if (bytesLeft == 0)
return;
}
}
}
/**
* Write memptr data into the bucket
*
* @param ptr the source memptr
*/
private void writeBytes(memptr ptr)
{
long bytesLeft = ptr.lengthOf();
long bytesWritten = 0;
while (bytesLeft > bytesWritten)
{
long numBytes = Math.min(bytesLeft, Integer.MAX_VALUE);
numBytes = writeBytes(ptr.asByteArray(bytesWritten, numBytes), (int) numBytes);
bytesWritten += numBytes;
}
this.position.assign(this.position.longValue() + bytesWritten);
}
/**
* Write the content of byte array.
*
* The method updates the size but not the position because putBytes form ByteBucket does not
* change the current position.
*
* @param data the byte array data
* @param len the length of data to write
*
* @return the number of bytes written
*/
private int writeBytes(byte[] data, int len)
{
int bytesLeft = Math.min(data.length, len);
int bytesWritten = 0;
int crtBucket = (int) (writePosition / MAX_BYTES_PER_ROW);
int endBucket = (int) ((getSize().longValue()) / MAX_BYTES_PER_ROW);
int endOffset = (int) ((size.longValue()) % MAX_BYTES_PER_ROW);
if (writePosition + bytesLeft > Integer.MAX_VALUE)
{
ErrorManager.recordOrThrowError(0,
"Cannot expand memory allocation because it exceeds the limit", false);
return 0;
}
else
{
while (bytesLeft > 0)
{
byte[] bucket;
if (buckets.size() > crtBucket)
{
bucket = buckets.get(crtBucket);
}
else
{
bucket = new byte[MAX_BYTES_PER_ROW];
buckets.add(bucket);
}
int pos = crtBucket == endBucket ? endOffset : 0;
int numBytes = Math.min(MAX_BYTES_PER_ROW - pos, bytesLeft);
bytesLeft -= numBytes;
if (numBytes > 0)
{
System.arraycopy(data, bytesWritten, bucket, pos, numBytes);
bytesWritten += numBytes;
}
crtBucket++;
}
writePosition += bytesWritten;
size.assign(this.size.longValue() + bytesWritten);
return bytesWritten;
}
}
}