Refactor core APIs, fix bugs, and improve safety

- Make image result/info structs readonly; improve error handling and memory safety in image library
- Introduce IJobScheduler interface; move job scheduling docs to interface
- Remove "index 0 invalid" convention from slot/sparse maps; fix Count logic
- Add Owner<T> for disposable value types in low-level utilities
- Improve ObjectPool<T> thread safety and logic
- Change List<T>.RemoveAndSwapBack to return bool
- Remove unsafe methods from generated math types; add debug range checks
- Update benchmarks and enable collection checks in tests
- Improve documentation, comments, and error messages
- Bump assembly versions across all projects
This commit is contained in:
2025-12-21 16:08:10 +09:00
parent a1ad0bd2da
commit 1fee890329
38 changed files with 1967 additions and 350 deletions

View File

@@ -15,7 +15,7 @@ namespace Misaki.HighPerformance.Image
public const int STBI__F_avg_first = 5;
public static byte[] first_row_filter =
{ STBI__F_none, STBI__F_sub, STBI__F_none, STBI__F_avg_first, STBI__F_sub };
[STBI__F_none, STBI__F_sub, STBI__F_none, STBI__F_avg_first, STBI__F_sub];
public static byte[] stbi__check_png_header_png_sig = { 137, 80, 78, 71, 13, 10, 26, 10 };
public static byte[] stbi__depth_scale_table = { 0, 0xff, 0x55, 0, 0x11, 0, 0, 0, 0x01 };
@@ -48,7 +48,9 @@ namespace Misaki.HighPerformance.Image
var p = new stbi__png();
p.s = s;
if (stbi__png_info_raw(p, null, null, null) == 0)
{
return 0;
}
if (p.depth != 16)
{
stbi__rewind(p.s);
@@ -70,8 +72,12 @@ namespace Misaki.HighPerformance.Image
{
var i = 0;
for (i = 0; i < 8; ++i)
{
if (stbi__get8(s) != stbi__check_png_header_png_sig[i])
{
return stbi__err("bad png sig");
}
}
return 1;
}
@@ -90,12 +96,15 @@ namespace Misaki.HighPerformance.Image
{
var i = 0;
if (img_n == 1)
{
for (i = (int)(x - 1); i >= 0; --i)
{
dest[i * 2 + 1] = 255;
dest[i * 2 + 0] = src[i];
}
}
else
{
for (i = (int)(x - 1); i >= 0; --i)
{
dest[i * 4 + 3] = 255;
@@ -103,6 +112,7 @@ namespace Misaki.HighPerformance.Image
dest[i * 4 + 1] = src[i * 3 + 1];
dest[i * 4 + 0] = src[i * 3 + 0];
}
}
}
@@ -123,20 +133,36 @@ namespace Misaki.HighPerformance.Image
var output_bytes = out_n * bytes;
var filter_bytes = img_n * bytes;
var width = (int)x;
a._out_ = (byte*)stbi__malloc_mad3((int)x, (int)y, output_bytes, 0);
if (a._out_ == null)
{
return stbi__err("outofmem");
}
if (stbi__mad3sizes_valid(img_n, (int)x, depth, 7) == 0)
{
return stbi__err("too large");
}
img_width_bytes = (uint)((img_n * x * depth + 7) >> 3);
if (stbi__mad2sizes_valid((int)img_width_bytes, (int)y, (int)img_width_bytes) == 0)
{
return stbi__err("too large");
}
img_len = (img_width_bytes + 1) * y;
if (raw_len < img_len)
{
return stbi__err("not enough pixels");
}
filter_buf = (byte*)stbi__malloc_mad2((int)img_width_bytes, 2, 0);
if (filter_buf == null)
{
return stbi__err("outofmem");
}
if (depth < 8)
{
filter_bytes = 1;
@@ -157,7 +183,10 @@ namespace Misaki.HighPerformance.Image
}
if (j == 0)
{
filter = first_row_filter[filter];
}
switch (filter)
{
case STBI__F_none:
@@ -166,35 +195,49 @@ namespace Misaki.HighPerformance.Image
case STBI__F_sub:
CRuntime.memcpy(cur, raw, (ulong)filter_bytes);
for (k = filter_bytes; k < nk; ++k)
{
cur[k] = (byte)((raw[k] + cur[k - filter_bytes]) & 255);
}
break;
case STBI__F_up:
for (k = 0; k < nk; ++k)
{
cur[k] = (byte)((raw[k] + prior[k]) & 255);
}
break;
case STBI__F_avg:
for (k = 0; k < filter_bytes; ++k)
{
cur[k] = (byte)((raw[k] + (prior[k] >> 1)) & 255);
}
for (k = filter_bytes; k < nk; ++k)
{
cur[k] = (byte)((raw[k] + ((prior[k] + cur[k - filter_bytes]) >> 1)) & 255);
}
break;
case STBI__F_paeth:
for (k = 0; k < filter_bytes; ++k)
{
cur[k] = (byte)((raw[k] + prior[k]) & 255);
}
for (k = filter_bytes; k < nk; ++k)
{
cur[k] = (byte)((raw[k] + stbi__paeth(cur[k - filter_bytes], prior[k],
prior[k - filter_bytes])) & 255);
}
break;
case STBI__F_avg_first:
CRuntime.memcpy(cur, raw, (ulong)filter_bytes);
for (k = filter_bytes; k < nk; ++k)
{
cur[k] = (byte)((raw[k] + (cur[k - filter_bytes] >> 1)) & 255);
}
break;
}
@@ -208,39 +251,60 @@ namespace Misaki.HighPerformance.Image
byte inb = 0;
var nsmp = (uint)(x * img_n);
if (depth == 4)
{
for (i = 0; i < nsmp; ++i)
{
if ((i & 1) == 0)
{
inb = *_in_++;
}
*_out_++ = (byte)(scale * (inb >> 4));
inb <<= 4;
}
}
else if (depth == 2)
{
for (i = 0; i < nsmp; ++i)
{
if ((i & 3) == 0)
{
inb = *_in_++;
}
*_out_++ = (byte)(scale * (inb >> 6));
inb <<= 2;
}
}
else
{
for (i = 0; i < nsmp; ++i)
{
if ((i & 7) == 0)
{
inb = *_in_++;
}
*_out_++ = (byte)(scale * (inb >> 7));
inb <<= 1;
}
}
if (img_n != out_n)
{
stbi__create_png_alpha_expand8(dest, dest, x, img_n);
}
}
else if (depth == 8)
{
if (img_n == out_n)
{
CRuntime.memcpy(dest, cur, (ulong)(x * img_n));
}
else
{
stbi__create_png_alpha_expand8(dest, cur, x, img_n);
}
}
else if (depth == 16)
{
@@ -249,17 +313,22 @@ namespace Misaki.HighPerformance.Image
if (img_n == out_n)
{
for (i = 0; i < nsmp; ++i, ++dest16, cur += 2)
{
*dest16 = (ushort)((cur[0] << 8) | cur[1]);
}
}
else
{
if (img_n == 1)
{
for (i = 0; i < x; ++i, dest16 += 2, cur += 2)
{
dest16[0] = (ushort)((cur[0] << 8) | cur[1]);
dest16[1] = 0xffff;
}
}
else
{
for (i = 0; i < x; ++i, dest16 += 4, cur += 6)
{
dest16[0] = (ushort)((cur[0] << 8) | cur[1]);
@@ -267,13 +336,17 @@ namespace Misaki.HighPerformance.Image
dest16[2] = (ushort)((cur[4] << 8) | cur[5]);
dest16[3] = 0xffff;
}
}
}
}
}
CRuntime.free(filter_buf);
if (all_ok == 0)
{
return 0;
}
return 1;
}
@@ -285,17 +358,23 @@ namespace Misaki.HighPerformance.Image
byte* final;
var p = 0;
if (interlaced == 0)
return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a.s.img_x, a.s.img_y, depth,
color);
{
return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a.s.img_x, a.s.img_y, depth, color);
}
final = (byte*)stbi__malloc_mad3((int)a.s.img_x, (int)a.s.img_y, out_bytes, 0);
if (final == null)
{
return stbi__err("outofmem");
}
var xorig = stackalloc int[] { 0, 4, 0, 2, 0, 1, 0 };
var yorig = stackalloc int[] { 0, 0, 4, 0, 2, 0, 1 };
var xspc = stackalloc int[] { 8, 8, 4, 4, 2, 2, 1 };
var yspc = stackalloc int[] { 8, 8, 8, 4, 4, 2, 2 };
for (p = 0; p < 7; ++p)
{
var xorig = stackalloc int[] { 0, 4, 0, 2, 0, 1, 0 };
var yorig = stackalloc int[] { 0, 0, 4, 0, 2, 0, 1 };
var xspc = stackalloc int[] { 8, 8, 4, 4, 2, 2, 1 };
var yspc = stackalloc int[] { 8, 8, 8, 4, 4, 2, 2 };
var i = 0;
var j = 0;
var x = 0;
@@ -313,6 +392,7 @@ namespace Misaki.HighPerformance.Image
}
for (j = 0; j < y; ++j)
{
for (i = 0; i < x; ++i)
{
var out_y = j * yspc[p] + yorig[p];
@@ -320,6 +400,7 @@ namespace Misaki.HighPerformance.Image
CRuntime.memcpy(final + out_y * a.s.img_x * out_bytes + out_x * out_bytes,
a._out_ + (j * x + i) * out_bytes, (ulong)out_bytes);
}
}
CRuntime.free(a._out_);
image_data += img_len;
@@ -338,18 +419,25 @@ namespace Misaki.HighPerformance.Image
var pixel_count = s.img_x * s.img_y;
var p = z._out_;
if (out_n == 2)
{
for (i = 0; i < pixel_count; ++i)
{
p[1] = (byte)(p[0] == tc[0] ? 0 : 255);
p += 2;
}
}
else
{
for (i = 0; i < pixel_count; ++i)
{
if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2])
{
p[3] = 0;
}
p += 4;
}
}
return 1;
}
@@ -361,18 +449,25 @@ namespace Misaki.HighPerformance.Image
var pixel_count = s.img_x * s.img_y;
var p = (ushort*)z._out_;
if (out_n == 2)
{
for (i = 0; i < pixel_count; ++i)
{
p[1] = (ushort)(p[0] == tc[0] ? 0 : 65535);
p += 2;
}
}
else
{
for (i = 0; i < pixel_count; ++i)
{
if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2])
{
p[3] = 0;
}
p += 4;
}
}
return 1;
}
@@ -386,9 +481,12 @@ namespace Misaki.HighPerformance.Image
var orig = a._out_;
p = (byte*)stbi__malloc_mad2((int)pixel_count, pal_img_n, 0);
if (p == null)
{
return stbi__err("outofmem");
}
temp_out = p;
if (pal_img_n == 3)
{
for (i = 0; i < pixel_count; ++i)
{
var n = orig[i] * 4;
@@ -397,7 +495,9 @@ namespace Misaki.HighPerformance.Image
p[2] = palette[n + 2];
p += 3;
}
}
else
{
for (i = 0; i < pixel_count; ++i)
{
var n = orig[i] * 4;
@@ -407,6 +507,7 @@ namespace Misaki.HighPerformance.Image
p[3] = palette[n + 3];
p += 4;
}
}
CRuntime.free(a._out_);
a._out_ = temp_out;
@@ -423,9 +524,7 @@ namespace Misaki.HighPerformance.Image
{
for (i = 0; i < pixel_count; ++i)
{
var t = p[0];
p[0] = p[2];
p[2] = t;
(p[2], p[0]) = (p[0], p[2]);
p += 3;
}
}
@@ -434,6 +533,7 @@ namespace Misaki.HighPerformance.Image
if ((stbi__unpremultiply_on_load_set != 0
? stbi__unpremultiply_on_load_local
: stbi__unpremultiply_on_load_global) != 0)
{
for (i = 0; i < pixel_count; ++i)
{
var a = p[3];
@@ -453,14 +553,15 @@ namespace Misaki.HighPerformance.Image
p += 4;
}
}
else
{
for (i = 0; i < pixel_count; ++i)
{
var t = p[0];
p[0] = p[2];
p[2] = t;
(p[2], p[0]) = (p[0], p[2]);
p += 4;
}
}
}
}
@@ -484,10 +585,17 @@ namespace Misaki.HighPerformance.Image
z.expanded = null;
z.idata = null;
z._out_ = null;
if (stbi__check_png_header(s) == 0)
{
return 0;
}
if (scan == STBI__SCAN_type)
{
return 1;
}
for (; ; )
{
var c = stbi__get_chunk_header(s);
@@ -502,50 +610,92 @@ namespace Misaki.HighPerformance.Image
var comp = 0;
var filter = 0;
if (first == 0)
{
return stbi__err("multiple IHDR");
}
first = 0;
if (c.length != 13)
{
return stbi__err("bad IHDR len");
}
s.img_x = stbi__get32be(s);
s.img_y = stbi__get32be(s);
if (s.img_y > 1 << 24)
{
return stbi__err("too large");
}
if (s.img_x > 1 << 24)
{
return stbi__err("too large");
}
z.depth = stbi__get8(s);
if (z.depth != 1 && z.depth != 2 && z.depth != 4 && z.depth != 8 && z.depth != 16)
{
return stbi__err("1/2/4/8/16-bit only");
}
color = stbi__get8(s);
if (color > 6)
{
return stbi__err("bad ctype");
}
if (color == 3 && z.depth == 16)
{
return stbi__err("bad ctype");
}
if (color == 3)
{
pal_img_n = 3;
}
else if ((color & 1) != 0)
{
return stbi__err("bad ctype");
}
comp = stbi__get8(s);
if (comp != 0)
{
return stbi__err("bad comp method");
}
filter = stbi__get8(s);
if (filter != 0)
{
return stbi__err("bad filter method");
}
interlace = stbi__get8(s);
if (interlace > 1)
{
return stbi__err("bad interlace method");
}
if (s.img_x == 0 || s.img_y == 0)
{
return stbi__err("0-pixel image");
}
if (pal_img_n == 0)
{
s.img_n = ((color & 2) != 0 ? 3 : 1) + ((color & 4) != 0 ? 1 : 0);
if ((1 << 30) / s.img_x / s.img_n < s.img_y)
{
return stbi__err("too large");
}
}
else
{
s.img_n = 1;
if ((1 << 30) / s.img_x / 4 < s.img_y)
{
return stbi__err("too large");
}
}
break;
@@ -554,12 +704,21 @@ namespace Misaki.HighPerformance.Image
case ((uint)80 << 24) + ((uint)76 << 16) + ((uint)84 << 8) + 69:
{
if (first != 0)
{
return stbi__err("first not IHDR");
}
if (c.length > 256 * 3)
{
return stbi__err("invalid PLTE");
}
pal_len = c.length / 3;
if (pal_len * 3 != c.length)
{
return stbi__err("invalid PLTE");
}
for (i = 0; i < pal_len; ++i)
{
palette[i * 4 + 0] = stbi__get8(s);
@@ -574,9 +733,15 @@ namespace Misaki.HighPerformance.Image
case ((uint)116 << 24) + ((uint)82 << 16) + ((uint)78 << 8) + 83:
{
if (first != 0)
{
return stbi__err("first not IHDR");
}
if (z.idata != null)
{
return stbi__err("tRNS after IDAT");
}
if (pal_img_n != 0)
{
if (scan == STBI__SCAN_header)
@@ -586,19 +751,33 @@ namespace Misaki.HighPerformance.Image
}
if (pal_len == 0)
{
return stbi__err("tRNS before PLTE");
}
if (c.length > pal_len)
{
return stbi__err("bad tRNS len");
}
pal_img_n = 4;
for (i = 0; i < c.length; ++i)
{
palette[i * 4 + 3] = stbi__get8(s);
}
}
else
{
if ((s.img_n & 1) == 0)
{
return stbi__err("tRNS with alpha");
}
if (c.length != (uint)s.img_n * 2)
{
return stbi__err("bad tRNS len");
}
has_trans = 1;
if (scan == STBI__SCAN_header)
{
@@ -607,11 +786,19 @@ namespace Misaki.HighPerformance.Image
}
if (z.depth == 16)
{
for (k = 0; k < s.img_n && k < 3; ++k)
{
tc16[k] = (ushort)stbi__get16be(s);
}
}
else
{
for (k = 0; k < s.img_n && k < 3; ++k)
{
tc[k] = (byte)((byte)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z.depth]);
}
}
}
break;
@@ -620,37 +807,63 @@ namespace Misaki.HighPerformance.Image
case ((uint)73 << 24) + ((uint)68 << 16) + ((uint)65 << 8) + 84:
{
if (first != 0)
{
return stbi__err("first not IHDR");
}
if (pal_img_n != 0 && pal_len == 0)
{
return stbi__err("no PLTE");
}
if (scan == STBI__SCAN_header)
{
if (pal_img_n != 0)
{
s.img_n = pal_img_n;
}
return 1;
}
if (c.length > 1u << 30)
{
return stbi__err("IDAT size limit");
}
if ((int)(ioff + c.length) < (int)ioff)
{
return 0;
}
if (ioff + c.length > idata_limit)
{
var idata_limit_old = idata_limit;
byte* p;
if (idata_limit == 0)
{
idata_limit = c.length > 4096 ? c.length : 4096;
}
while (ioff + c.length > idata_limit)
{
idata_limit *= 2;
}
p = (byte*)CRuntime.realloc(z.idata, (ulong)idata_limit);
if (p == null)
{
return stbi__err("outofmem");
}
z.idata = p;
}
if (stbi__getn(s, z.idata + ioff, (int)c.length) == 0)
{
return stbi__err("outofdata");
}
ioff += c.length;
break;
}
@@ -660,36 +873,60 @@ namespace Misaki.HighPerformance.Image
uint raw_len = 0;
uint bpl = 0;
if (first != 0)
{
return stbi__err("first not IHDR");
}
if (scan != STBI__SCAN_load)
{
return 1;
}
if (z.idata == null)
{
return stbi__err("no IDAT");
}
bpl = (uint)((s.img_x * z.depth + 7) / 8);
raw_len = (uint)(bpl * s.img_y * s.img_n + s.img_y);
z.expanded = (byte*)stbi_zlib_decode_malloc_guesssize_headerflag((sbyte*)z.idata, (int)ioff,
(int)raw_len, (int*)&raw_len, is_iphone == 0 ? 1 : 0);
if (z.expanded == null)
{
return 0;
}
CRuntime.free(z.idata);
z.idata = null;
if ((req_comp == s.img_n + 1 && req_comp != 3 && pal_img_n == 0) || has_trans != 0)
{
s.img_out_n = s.img_n + 1;
}
else
{
s.img_out_n = s.img_n;
}
if (stbi__create_png_image(z, z.expanded, raw_len, s.img_out_n, z.depth, color, interlace) == 0)
{
return 0;
}
if (has_trans != 0)
{
if (z.depth == 16)
{
if (stbi__compute_transparency16(z, tc16, s.img_out_n) == 0)
{
return 0;
}
}
else
{
if (stbi__compute_transparency(z, tc, s.img_out_n) == 0)
{
return 0;
}
}
}
@@ -697,15 +934,23 @@ namespace Misaki.HighPerformance.Image
(stbi__de_iphone_flag_set != 0
? stbi__de_iphone_flag_local
: stbi__de_iphone_flag_global) != 0 && s.img_out_n > 2)
{
stbi__de_iphone(z);
}
if (pal_img_n != 0)
{
s.img_n = pal_img_n;
s.img_out_n = pal_img_n;
if (req_comp >= 3)
{
s.img_out_n = req_comp;
}
if (stbi__expand_png_palette(z, palette, (int)pal_len, s.img_out_n) == 0)
{
return 0;
}
}
else if (has_trans != 0)
{
@@ -720,7 +965,10 @@ namespace Misaki.HighPerformance.Image
default:
if (first != 0)
{
return stbi__err("first not IHDR");
}
if ((c.type & (1 << 29)) == 0)
{
stbi__parse_png_file_invalid_chunk[0] = (char)((c.type >> 24) & 255);
@@ -742,32 +990,51 @@ namespace Misaki.HighPerformance.Image
{
void* result = null;
if (req_comp < 0 || req_comp > 4)
{
return (byte*)(ulong)(stbi__err("bad req_comp") != 0 ? 0 : 0);
}
if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp) != 0)
{
if (p.depth <= 8)
{
ri->bits_per_channel = 8;
}
else if (p.depth == 16)
{
ri->bits_per_channel = 16;
}
else
{
return (byte*)(ulong)(stbi__err("bad bits_per_channel") != 0 ? 0 : 0);
}
result = p._out_;
p._out_ = null;
if (req_comp != 0 && req_comp != p.s.img_out_n)
{
if (ri->bits_per_channel == 8)
{
result = stbi__convert_format((byte*)result, p.s.img_out_n, req_comp, p.s.img_x, p.s.img_y);
}
else
{
result = stbi__convert_format16((ushort*)result, p.s.img_out_n, req_comp, p.s.img_x, p.s.img_y);
}
p.s.img_out_n = req_comp;
if (result == null)
{
return result;
}
}
*x = (int)p.s.img_x;
*y = (int)p.s.img_y;
if (n != null)
{
*n = p.s.img_n;
}
}
CRuntime.free(p._out_);
@@ -788,11 +1055,20 @@ namespace Misaki.HighPerformance.Image
}
if (x != null)
{
*x = (int)p.s.img_x;
}
if (y != null)
{
*y = (int)p.s.img_y;
}
if (comp != null)
{
*comp = p.s.img_n;
}
return 1;
}