blob: f87c2ec70eea2f7638850b4792e5792aa007968f [file] [log] [blame]
// 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;
using System.Collections.Generic;
using Apache.Arrow.Memory;
using Apache.Arrow.Types;
namespace Apache.Arrow
{
public class BooleanArray : Array, IReadOnlyList<bool?>, ICollection<bool?>
{
public class Builder : IArrowArrayBuilder<bool, BooleanArray, Builder>
{
private ArrowBuffer.BitmapBuilder ValueBuffer { get; }
private ArrowBuffer.BitmapBuilder ValidityBuffer { get; }
public int Length => ValueBuffer.Length;
public int Capacity => ValueBuffer.Capacity;
public int NullCount => ValidityBuffer.UnsetBitCount;
public Builder()
{
ValueBuffer = new ArrowBuffer.BitmapBuilder();
ValidityBuffer = new ArrowBuffer.BitmapBuilder();
}
public Builder Append(bool value)
{
return NullableAppend(value);
}
public Builder NullableAppend(bool? value)
{
// Note that we rely on the fact that null values are false in the value buffer.
ValueBuffer.Append(value ?? false);
ValidityBuffer.Append(value.HasValue);
return this;
}
public Builder Append(ReadOnlySpan<bool> span)
{
foreach (bool value in span)
{
Append(value);
}
return this;
}
public Builder AppendRange(IEnumerable<bool> values)
{
foreach (bool value in values)
{
Append(value);
}
return this;
}
public Builder AppendNull()
{
return NullableAppend(null);
}
public BooleanArray Build(MemoryAllocator allocator = default)
{
ArrowBuffer validityBuffer = NullCount > 0
? ValidityBuffer.Build(allocator)
: ArrowBuffer.Empty;
return new BooleanArray(
ValueBuffer.Build(allocator), validityBuffer,
Length, NullCount, 0);
}
public Builder Clear()
{
ValueBuffer.Clear();
ValidityBuffer.Clear();
return this;
}
public Builder Reserve(int capacity)
{
if (capacity < 0)
{
throw new ArgumentOutOfRangeException(nameof(capacity));
}
ValueBuffer.Reserve(capacity);
ValidityBuffer.Reserve(capacity);
return this;
}
public Builder Resize(int length)
{
if (length < 0)
{
throw new ArgumentOutOfRangeException(nameof(length));
}
ValueBuffer.Resize(length);
ValidityBuffer.Resize(length);
return this;
}
public Builder Toggle(int index)
{
CheckIndex(index);
// If there is a null at this index, assume it was set to false in the value buffer, and so becomes
// true/non-null after toggling.
ValueBuffer.Toggle(index);
ValidityBuffer.Set(index);
return this;
}
public Builder Set(int index)
{
CheckIndex(index);
ValueBuffer.Set(index);
ValidityBuffer.Set(index);
return this;
}
public Builder Set(int index, bool value)
{
CheckIndex(index);
ValueBuffer.Set(index, value);
ValidityBuffer.Set(index);
return this;
}
public Builder Swap(int i, int j)
{
CheckIndex(i);
CheckIndex(j);
ValueBuffer.Swap(i, j);
ValidityBuffer.Swap(i, j);
return this;
}
private void CheckIndex(int index)
{
if (index < 0 || index >= Length)
{
throw new ArgumentOutOfRangeException(nameof(index));
}
}
}
public ArrowBuffer ValueBuffer => Data.Buffers[1];
public ReadOnlySpan<byte> Values => ValueBuffer.Span.Slice(0, (int)Math.Ceiling(Length / 8.0));
public BooleanArray(
ArrowBuffer valueBuffer, ArrowBuffer nullBitmapBuffer,
int length, int nullCount, int offset)
: this(new ArrayData(BooleanType.Default, length, nullCount, offset,
new[] { nullBitmapBuffer, valueBuffer }))
{ }
public BooleanArray(ArrayData data)
: base(data)
{
data.EnsureDataType(ArrowTypeId.Boolean);
}
public override void Accept(IArrowArrayVisitor visitor) => Accept(this, visitor);
[Obsolete("GetBoolean does not support null values. Use GetValue instead (which this method invokes internally).")]
public bool GetBoolean(int index)
{
return GetValue(index).GetValueOrDefault();
}
public bool? GetValue(int index)
{
return IsNull(index)
? null
: BitUtility.GetBit(ValueBuffer.Span, index + Offset);
}
int IReadOnlyCollection<bool?>.Count => Length;
bool? IReadOnlyList<bool?>.this[int index] => GetValue(index);
IEnumerator<bool?> IEnumerable<bool?>.GetEnumerator()
{
for (int index = 0; index < Length; index++)
{
yield return GetValue(index);
}
}
IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable<bool?>)this).GetEnumerator();
int ICollection<bool?>.Count => Length;
bool ICollection<bool?>.IsReadOnly => true;
void ICollection<bool?>.Add(bool? item) => throw new NotSupportedException("Collection is read-only.");
bool ICollection<bool?>.Remove(bool? item) => throw new NotSupportedException("Collection is read-only.");
void ICollection<bool?>.Clear() => throw new NotSupportedException("Collection is read-only.");
bool ICollection<bool?>.Contains(bool? item)
{
for (int index = 0; index < Length; index++)
{
if (GetValue(index).Equals(item))
return true;
}
return false;
}
void ICollection<bool?>.CopyTo(bool?[] array, int arrayIndex)
{
for (int srcIndex = 0, destIndex = arrayIndex; srcIndex < Length; srcIndex++, destIndex++)
{
array[destIndex] = GetValue(srcIndex);
}
}
}
}