blob: 21ae1b434f7c043acc0c341dc83ee3953bcc4ac9 [file]
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You 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.
using System;
using System.Collections.Generic;
using Apache.Arrow.Scalars.Variant;
namespace Apache.Arrow.Variant.Benchmarks
{
/// <summary>
/// Struct variant value with inline storage for numeric primitives.
/// Numeric types are stored in _inlineValue (no boxing), reference types
/// in _objectValue.
/// Size on x64: 24 bytes inline (VariantPrimitiveType + long + object ref).
/// </summary>
public readonly struct StructVariantValue : IEquatable<StructVariantValue>
{
private readonly VariantPrimitiveType _primitiveType;
private readonly long _inlineValue;
private readonly object _objectValue;
private StructVariantValue(VariantPrimitiveType primitiveType, long inlineValue)
{
_primitiveType = primitiveType;
_inlineValue = inlineValue;
_objectValue = null;
}
private StructVariantValue(VariantPrimitiveType primitiveType, object objectValue)
{
_primitiveType = primitiveType;
_inlineValue = 0;
_objectValue = objectValue;
}
private const VariantPrimitiveType ObjectSentinel = (VariantPrimitiveType)254;
private const VariantPrimitiveType ArraySentinel = (VariantPrimitiveType)255;
// default(StructVariantValue) has _primitiveType = NullType = 0
public static readonly StructVariantValue Null = default;
// ---------------------------------------------------------------
// Factory methods — inline storage (no boxing)
// ---------------------------------------------------------------
public static StructVariantValue FromBoolean(bool value) =>
new StructVariantValue(
value ? VariantPrimitiveType.BooleanTrue : VariantPrimitiveType.BooleanFalse,
0L);
public static StructVariantValue FromInt32(int value) =>
new StructVariantValue(VariantPrimitiveType.Int32, (long)value);
public static StructVariantValue FromInt64(long value) =>
new StructVariantValue(VariantPrimitiveType.Int64, value);
public static StructVariantValue FromFloat(float value) =>
new StructVariantValue(VariantPrimitiveType.Float, (long)BitConverter.SingleToInt32Bits(value));
public static StructVariantValue FromDouble(double value) =>
new StructVariantValue(VariantPrimitiveType.Double, BitConverter.DoubleToInt64Bits(value));
// ---------------------------------------------------------------
// Factory methods — object storage
// ---------------------------------------------------------------
public static StructVariantValue FromString(string value) =>
new StructVariantValue(VariantPrimitiveType.String, value);
public static StructVariantValue FromObject(Dictionary<string, StructVariantValue> fields) =>
new StructVariantValue(ObjectSentinel, fields);
public static StructVariantValue FromArray(List<StructVariantValue> elements) =>
new StructVariantValue(ArraySentinel, elements);
// ---------------------------------------------------------------
// Accessors (minimal — no type guards for benchmark hot paths)
// ---------------------------------------------------------------
public VariantPrimitiveType PrimitiveType => _primitiveType;
public bool IsNull => _primitiveType == VariantPrimitiveType.NullType;
public bool AsBoolean() => _primitiveType == VariantPrimitiveType.BooleanTrue;
public int AsInt32() => (int)_inlineValue;
public long AsInt64() => _inlineValue;
public float AsFloat() => BitConverter.Int32BitsToSingle((int)_inlineValue);
public double AsDouble() => BitConverter.Int64BitsToDouble(_inlineValue);
public string AsString() => (string)_objectValue;
public Dictionary<string, StructVariantValue> AsObject() =>
(Dictionary<string, StructVariantValue>)_objectValue;
public List<StructVariantValue> AsArray() =>
(List<StructVariantValue>)_objectValue;
// ---------------------------------------------------------------
// Equality
// ---------------------------------------------------------------
public bool Equals(StructVariantValue other)
{
if (_primitiveType != other._primitiveType) return false;
// For inline types (_objectValue is null), compare the long directly
if (_objectValue == null && other._objectValue == null)
return _inlineValue == other._inlineValue;
if (_objectValue == null || other._objectValue == null) return false;
return _objectValue.Equals(other._objectValue);
}
public override bool Equals(object obj) =>
obj is StructVariantValue other && Equals(other);
public override int GetHashCode() =>
_objectValue == null
? _primitiveType.GetHashCode() ^ _inlineValue.GetHashCode()
: _primitiveType.GetHashCode() ^ _objectValue.GetHashCode();
public static bool operator ==(StructVariantValue left, StructVariantValue right) =>
left.Equals(right);
public static bool operator !=(StructVariantValue left, StructVariantValue right) =>
!left.Equals(right);
}
}