Changed to return value container for available cap value to prevent long range enumerations.

This commit is contained in:
Eugene Wang
2025-11-20 13:18:21 -05:00
parent 36c7dd11ca
commit 8949b36833
7 changed files with 229 additions and 147 deletions

View File

@@ -2,7 +2,7 @@
<PropertyGroup>
<!--change these in each release-->
<VersionPrefix>4.0.0.0</VersionPrefix>
<VersionSuffix>alpha.16</VersionSuffix>
<VersionSuffix>alpha.17</VersionSuffix>
<!--keep it the same until major # changes-->
<AssemblyVersion>4.0.0.0</AssemblyVersion>

View File

@@ -41,14 +41,10 @@ namespace NTwain.Caps
}
}
public IList<TValue> Get()
public ValueContainer<TValue> Get()
{
LastSTS = _twain.GetCapValues(Cap, out IList<TValue> values);
if (LastSTS.IsSuccess)
{
return values;
};
return Array.Empty<TValue>();
LastSTS = _twain.GetCapValues(Cap, out ValueContainer<TValue> value);
return value;
}
public IList<TValue> GetCurrent()
@@ -57,7 +53,7 @@ namespace NTwain.Caps
if (LastSTS.IsSuccess)
{
return value;
};
}
return Array.Empty<TValue>();
}
@@ -67,7 +63,7 @@ namespace NTwain.Caps
if (LastSTS.IsSuccess)
{
return value;
};
}
return Array.Empty<TValue>();
}
@@ -77,7 +73,7 @@ namespace NTwain.Caps
if (LastSTS.IsSuccess)
{
return value;
};
}
return default;
}
@@ -87,7 +83,7 @@ namespace NTwain.Caps
if (LastSTS.IsSuccess)
{
return value;
};
}
return default;
}
@@ -97,7 +93,7 @@ namespace NTwain.Caps
if (LastSTS.IsSuccess)
{
return value;
};
}
return Array.Empty<string>();
}
}

View File

@@ -0,0 +1,66 @@
using NTwain.Data;
using System.Collections.Generic;
using System.Linq;
namespace NTwain.Caps;
public record ValueContainer<TValue> where TValue : struct
{
public TWON ContainerType { get; set; }
public TValue? OneValue { get; set; }
public IList<TValue>? ArrayValue { get; set; }
public EnumValue<TValue>? EnumValue { get; set; }
public RangeValue<TValue>? RangeValue { get; set; }
public IEnumerable<TValue> GetValues()
{
return ContainerType switch
{
TWON.ONEVALUE => OneValue.HasValue ? ToEnumerable(OneValue.Value) : [],
TWON.ARRAY => ArrayValue ?? [],
TWON.ENUMERATION => EnumValue?.Items ?? [],
TWON.RANGE => RangeValue != null ? GenerateRangeValues(RangeValue) : [],
_ => [],
};
}
private IEnumerable<TValue> ToEnumerable(TValue value)
{
yield return value;
}
private IEnumerable<TValue> GenerateRangeValues(RangeValue<TValue> range)
{
var de = new DynamicEnumerator<TValue>(range.Min, range.Max, range.Step);
while (de.MoveNext())
{
yield return de.Current;
}
}
}
public record EnumValue<TValue> where TValue : struct
{
public TValue[] Items { get; set; } = [];
public int CurrentIndex { get; set; }
public int DefaultIndex { get; set; }
}
public record RangeValue<TValue> where TValue : struct
{
public TValue Min { get; set; }
public TValue Max { get; set; }
public TValue Step { get; set; }
public TValue DefaultValue;
public TValue CurrentValue;
}

View File

@@ -0,0 +1,48 @@
using System.Collections.Generic;
namespace NTwain.Data;
// dynamic is a cheap hack to sidestep the compiler restrictions if I know TValue is numeric
class DynamicEnumerator<TValue> : IEnumerator<TValue> where TValue : struct
{
private readonly TValue _min;
private readonly TValue _max;
private readonly TValue _step;
private TValue _cur;
bool started = false;
public DynamicEnumerator(TValue min, TValue max, TValue step)
{
_min = min;
_max = max;
_step = step;
_cur = min;
}
public TValue Current => _cur;
object System.Collections.IEnumerator.Current => this.Current;
public void Dispose() { }
public bool MoveNext()
{
if (!started)
{
started = true;
return true;
}
var next = _cur + (dynamic)_step;
if (next == _cur || next < _min || next > _max) return false;
_cur = next;
return true;
}
public void Reset()
{
_cur = _min;
started = false;
}
}

View File

@@ -2590,7 +2590,9 @@ namespace NTwain.Data
ICONID = 962,
DSMID = 461,
DSMCODEID = 63
DSMCODEID = 63,
DONTCARE = 0xffff
}
///// <summary>

View File

@@ -284,58 +284,13 @@ namespace NTwain.Data
if (MinValue is not IConvertible)
throw new NotSupportedException($"The value type {typeof(TValue).Name} is not supported for range enumeration.");
return new DynamicEnumerator(MinValue, MaxValue, StepSize);
return new DynamicEnumerator<TValue>(MinValue, MaxValue, StepSize);
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return ((IEnumerable<TValue>)this).GetEnumerator();
}
// dynamic is a cheap hack to sidestep the compiler restrictions if I know TValue is numeric
class DynamicEnumerator : IEnumerator<TValue>
{
private readonly TValue _min;
private readonly TValue _max;
private readonly TValue _step;
private TValue _cur;
bool started = false;
public DynamicEnumerator(TValue min, TValue max, TValue step)
{
_min = min;
_max = max;
_step = step;
_cur = min;
}
public TValue Current => _cur;
object System.Collections.IEnumerator.Current => this.Current;
public void Dispose() { }
public bool MoveNext()
{
if (!started)
{
started = true;
return true;
}
var next = _cur + (dynamic)_step;
if (next == _cur || next < _min || next > _max) return false;
_cur = next;
return true;
}
public void Reset()
{
_cur = _min;
started = false;
}
}
}
partial struct TW_FIX32 : IEquatable<TW_FIX32>, IConvertible

View File

@@ -1,7 +1,6 @@
using NTwain.Caps;
using NTwain.Data;
using NTwain.Triplets;
using NTwain.Triplets.ControlDATs;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -178,31 +177,47 @@ namespace NTwain
/// <param name="cap"></param>
/// <param name="values"></param>
/// <returns></returns>
public STS GetCapValues<TValue>(CAP cap, out IList<TValue> values) where TValue : struct
public STS GetCapValues<TValue>(CAP cap, out ValueContainer<TValue> value) where TValue : struct
{
values = new List<TValue>();
var sts = GetCapValues(cap, out TW_CAPABILITY twcap);
value = new ValueContainer<TValue> { ContainerType = TWON.DONTCARE };
var sts = GetCapCurrent(cap, out TW_CAPABILITY twcap);
if (sts.RC == TWRC.SUCCESS)
{
value.ContainerType = twcap.ConType;
switch (twcap.ConType)
{
case TWON.ONEVALUE:
values.Add(twcap.ReadOneValue<TValue>(this));
value.OneValue = twcap.ReadOneValue<TValue>(this);
break;
case TWON.ENUMERATION:
var twenum = twcap.ReadEnumeration<TValue>(this);
if (twenum.Items != null && twenum.Items.Length > 0)
((List<TValue>)values).AddRange(twenum.Items);
if (twenum.Items != null)
{
value.EnumValue = new EnumValue<TValue>
{
CurrentIndex = twenum.CurrentIndex,
DefaultIndex = twenum.DefaultIndex,
Items = twenum.Items
};
}
break;
case TWON.RANGE:
// This can be slow
var twrange = twcap.ReadRange<TValue>(this);
((List<TValue>)values).AddRange(twrange);
var range = twcap.ReadRange<TValue>(this);
value.RangeValue = new RangeValue<TValue>
{
Min = range.MinValue,
Max = range.MaxValue,
Step = range.StepSize,
DefaultValue = range.DefaultValue,
CurrentValue = range.CurrentValue
};
break;
case TWON.ARRAY:
var twarr = twcap.ReadArray<TValue>(this);
if (twarr != null && twarr.Count > 0)
((List<TValue>)values).AddRange(twarr);
if (twarr != null)
{
value.ArrayValue = twarr;
}
break;
default:
twcap.Free(this); break;