This commit is contained in:
libretroadmin committed 2026-10-28 19:16:45 +01:00
1 parent c0d33c5f17
commit d9597c359a
171 files changed
+38155 -1259

No files matched your search

+49 -29
View File
@@ -22,14 +22,14 @@
#include <retro_environment.h>
#if defined(__SSE2__)
#include <emmintrin.h>
#elif defined(__ALTIVEC__)
#include <altivec.h>
#endif
#include <audio/audio_mix.h>
#if defined(AUDIO_MIX_HAVE_SSE2)
#include <emmintrin.h>
#elif defined(AUDIO_MIX_HAVE_NEON)
#include <arm_neon.h>
#endif
void audio_mix_volume_C(float *s, const float *in, float vol, size_t len)
{
size_t i;
@@ -37,35 +37,55 @@ void audio_mix_volume_C(float *s, const float *in, float vol, size_t len)
s[i] += in[i] * vol;
}
#ifdef __SSE2__
/* The vector versions do the C loop's multiply and add per sample, so
* they give the same sums (but for denormals, which 32-bit ARM's NEON
* takes as zero). */
#if defined(AUDIO_MIX_HAVE_SSE2)
void audio_mix_volume_SSE2(float *s, const float *in, float vol, size_t len)
{
size_t i, remaining_samples;
__m128 volume = _mm_set1_ps(vol);
size_t i = 0;
__m128 volume = _mm_set1_ps(vol);
for (i = 0; i + 16 <= len; i += 16, s += 16, in += 16)
for (; i + 8 <= len; i += 8)
{
unsigned j;
__m128 input[4];
__m128 additive[4];
input[0] = _mm_loadu_ps(s + 0);
input[1] = _mm_loadu_ps(s + 4);
input[2] = _mm_loadu_ps(s + 8);
input[3] = _mm_loadu_ps(s + 12);
additive[0] = _mm_mul_ps(volume, _mm_loadu_ps(in + 0));
additive[1] = _mm_mul_ps(volume, _mm_loadu_ps(in + 4));
additive[2] = _mm_mul_ps(volume, _mm_loadu_ps(in + 8));
additive[3] = _mm_mul_ps(volume, _mm_loadu_ps(in + 12));
for (j = 0; j < 4; j++)
_mm_storeu_ps(s + 4 * j, _mm_add_ps(input[j], additive[j]));
__m128 a = _mm_add_ps(_mm_loadu_ps(s + i),
_mm_mul_ps(_mm_loadu_ps(in + i), volume));
__m128 b = _mm_add_ps(_mm_loadu_ps(s + i + 4),
_mm_mul_ps(_mm_loadu_ps(in + i + 4), volume));
_mm_storeu_ps(s + i, a);
_mm_storeu_ps(s + i + 4, b);
}
if (i + 4 <= len)
{
_mm_storeu_ps(s + i, _mm_add_ps(_mm_loadu_ps(s + i),
_mm_mul_ps(_mm_loadu_ps(in + i), volume)));
i += 4;
}
for (; i < len; i++)
s[i] += in[i] * vol;
}
#elif defined(AUDIO_MIX_HAVE_NEON)
void audio_mix_volume_NEON(float *s, const float *in, float vol, size_t len)
{
size_t i = 0;
float32x4_t volume = vdupq_n_f32(vol);
remaining_samples = len - i;
for (i = 0; i < remaining_samples; i++)
for (; i + 8 <= len; i += 8)
{
float32x4_t a = vaddq_f32(vld1q_f32(s + i),
vmulq_f32(vld1q_f32(in + i), volume));
float32x4_t b = vaddq_f32(vld1q_f32(s + i + 4),
vmulq_f32(vld1q_f32(in + i + 4), volume));
vst1q_f32(s + i, a);
vst1q_f32(s + i + 4, b);
}
if (i + 4 <= len)
{
vst1q_f32(s + i, vaddq_f32(vld1q_f32(s + i),
vmulq_f32(vld1q_f32(in + i), volume)));
i += 4;
}
for (; i < len; i++)
s[i] += in[i] * vol;
}
#endif
+168 -2
View File
@@ -19,6 +19,10 @@
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* retro_environment.h spells MSVC's SSE/SSE2 targets as __SSE__/__SSE2__,
* which the SIMD paths below test. */
#include <retro_environment.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
@@ -126,7 +130,131 @@ void convert_float_to_s16_init_simd(void)
float_to_s16_neon_enabled = true;
}
#else
void convert_float_to_s16(int16_t *s, const float *in, size_t len)
/* AVX2 and AVX-512 forms of the SSE2 loop below, built per function and
* chosen by convert_float_to_s16_init_simd(), so older CPUs keep SSE2
* and compilers without per-function targets leave them out. They run
* the SSE2 arithmetic step for step, so every target still yields the
* same s16 for the same float. */
#if defined(__SSE2__) && (defined(__x86_64__) || defined(__i386__) \
|| defined(_M_X64) || defined(_M_IX86))
#if defined(_MSC_VER) && !defined(__clang__)
#if _MSC_VER >= 1911
#define FLOAT_TO_S16_HAVE_WIDE 1
#define FLOAT_TO_S16_HAVE_AVX512 1
#define FLOAT_TO_S16_TARGET_AVX2
#define FLOAT_TO_S16_TARGET_AVX512
#endif
#elif defined(__clang__) && (defined(__SCE__) || defined(_MSC_VER))
/* Clang's immintrin.h for PlayStation and clang-cl only declares the
* intrinsics of ISAs enabled for the whole build, so per-function
* targets cannot reach them; build a tier only when the baseline has
* it (PS5's Zen 2 keeps AVX2, PS4's Jaguar has neither). */
#ifdef __AVX2__
#define FLOAT_TO_S16_HAVE_WIDE 1
#define FLOAT_TO_S16_TARGET_AVX2
#ifdef __AVX512F__
#define FLOAT_TO_S16_HAVE_AVX512 1
#define FLOAT_TO_S16_TARGET_AVX512
#endif
#endif
#elif (defined(__clang__) && __clang_major__ >= 4) \
|| (defined(__GNUC__) && !defined(__clang__) && __GNUC__ >= 5)
#define FLOAT_TO_S16_HAVE_WIDE 1
#define FLOAT_TO_S16_HAVE_AVX512 1
#define FLOAT_TO_S16_TARGET_AVX2 __attribute__((target("avx2")))
#define FLOAT_TO_S16_TARGET_AVX512 __attribute__((target("avx512f")))
#endif
#endif
#ifdef FLOAT_TO_S16_HAVE_WIDE
#include <immintrin.h>
/* 0 = SSE2, 1 = AVX2, 2 = AVX-512F; set once by init, before audio runs. */
static int float_to_s16_tier = 0;
#ifdef FLOAT_TO_S16_TEST_HOOKS
/* -1 leaves the tier alone; otherwise caps it for a test. */
int float_to_s16_tier_cap = -1;
#endif
#if defined(_MSC_VER) && !defined(__clang__)
#define FLOAT_TO_S16_ZEROUPPER() _mm256_zeroupper()
#else
#define FLOAT_TO_S16_ZEROUPPER()
#endif
/* Sixteen samples a step; returns how many it converted, leaving the
* rest (under 16) to the SSE2 loop and the scalar tail. */
FLOAT_TO_S16_TARGET_AVX2
static size_t float_to_s16_avx2(int16_t *s, const float *in, size_t len)
{
size_t i;
const __m256 factor = _mm256_set1_ps((float)0x8000);
const __m256 half = _mm256_set1_ps(0.5f);
const __m256 signmask = _mm256_castsi256_ps(
_mm256_set1_epi32((int)0x80000000));
const __m256 vmax = _mm256_set1_ps( 32767.0f);
const __m256 vmin = _mm256_set1_ps(-32768.0f);
for (i = 0; i + 16 <= len; i += 16)
{
__m256 a = _mm256_mul_ps(_mm256_loadu_ps(in + i), factor);
__m256 b = _mm256_mul_ps(_mm256_loadu_ps(in + i + 8), factor);
__m256i packed;
a = _mm256_and_ps(a, _mm256_cmp_ps(a, a, _CMP_ORD_Q));
b = _mm256_and_ps(b, _mm256_cmp_ps(b, b, _CMP_ORD_Q));
a = _mm256_max_ps(_mm256_min_ps(_mm256_add_ps(a, _mm256_or_ps(
_mm256_and_ps(a, signmask), half)), vmax), vmin);
b = _mm256_max_ps(_mm256_min_ps(_mm256_add_ps(b, _mm256_or_ps(
_mm256_and_ps(b, signmask), half)), vmax), vmin);
/* packs works per 128-bit lane: a0-3 b0-3 a4-7 b4-7; the
* permute restores sample order. */
packed = _mm256_packs_epi32(_mm256_cvttps_epi32(a),
_mm256_cvttps_epi32(b));
_mm256_storeu_si256((__m256i*)(s + i),
_mm256_permute4x64_epi64(packed, 0xD8));
}
FLOAT_TO_S16_ZEROUPPER();
return i;
}
/* Sixteen samples a step, the last step masked: the loads and the
* saturating narrow-and-store skip lanes past the end instead of a
* scalar tail, so it converts the whole buffer. */
#ifdef FLOAT_TO_S16_HAVE_AVX512
FLOAT_TO_S16_TARGET_AVX512
static void float_to_s16_avx512(int16_t *s, const float *in, size_t len)
{
size_t i;
const __m512 factor = _mm512_set1_ps((float)0x8000);
const __m512i half = _mm512_castps_si512(_mm512_set1_ps(0.5f));
const __m512i signmask = _mm512_set1_epi32((int)0x80000000);
const __m512 vmax = _mm512_set1_ps( 32767.0f);
const __m512 vmin = _mm512_set1_ps(-32768.0f);
for (i = 0; i < len; i += 16)
{
size_t left = len - i;
__mmask16 m = (left >= 16) ? (__mmask16)0xffff
: (__mmask16)((1u << left) - 1u);
__m512 x = _mm512_mul_ps(_mm512_maskz_loadu_ps(m, in + i),
factor);
__m512 bias;
/* NaN to +0, as the SSE2 cmpord/and does. */
x = _mm512_maskz_mov_ps(_mm512_cmp_ps_mask(x, x, _CMP_ORD_Q), x);
bias = _mm512_castsi512_ps(_mm512_or_si512(
_mm512_and_si512(_mm512_castps_si512(x), signmask), half));
x = _mm512_max_ps(_mm512_min_ps(_mm512_add_ps(x, bias), vmax),
vmin);
_mm512_mask_cvtsepi32_storeu_epi16(s + i, m, _mm512_cvttps_epi32(x));
}
FLOAT_TO_S16_ZEROUPPER();
}
#endif
#endif
/* The SSE2 / AltiVec / Allegrex loop and the scalar tail. */
static void convert_float_to_s16_base(int16_t *s, const float *in, size_t len)
{
size_t i = 0;
#if defined(__SSE2__)
@@ -271,5 +399,43 @@ void convert_float_to_s16(int16_t *s, const float *in, size_t len)
}
}
void convert_float_to_s16_init_simd(void) { }
void convert_float_to_s16(int16_t *s, const float *in, size_t len)
{
#ifdef FLOAT_TO_S16_HAVE_WIDE
int tier = float_to_s16_tier;
#ifdef FLOAT_TO_S16_TEST_HOOKS
if (float_to_s16_tier_cap >= 0 && tier > float_to_s16_tier_cap)
tier = float_to_s16_tier_cap;
#endif
#ifdef FLOAT_TO_S16_HAVE_AVX512
if (tier == 2)
{
float_to_s16_avx512(s, in, len);
return;
}
#endif
if (tier == 1)
{
size_t done = float_to_s16_avx2(s, in, len);
s += done;
in += done;
len -= done;
}
#endif
convert_float_to_s16_base(s, in, len);
}
void convert_float_to_s16_init_simd(void)
{
#ifdef FLOAT_TO_S16_HAVE_WIDE
uint64_t cpu = cpu_features_get();
float_to_s16_tier = 0;
if (cpu & RETRO_SIMD_AVX2)
float_to_s16_tier = 1;
#ifdef FLOAT_TO_S16_HAVE_AVX512
if (cpu & RETRO_SIMD_AVX512)
float_to_s16_tier = 2;
#endif
#endif
}
#endif
+4
View File
@@ -19,6 +19,10 @@
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* retro_environment.h spells MSVC's SSE/SSE2 targets as __SSE__/__SSE2__,
* which the SIMD paths below test. */
#include <retro_environment.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
+144 -2
View File
@@ -19,6 +19,10 @@
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* retro_environment.h spells MSVC's SSE/SSE2 targets as __SSE__/__SSE2__,
* which the SIMD paths below test. */
#include <retro_environment.h>
#if defined(__SSE2__)
#include <emmintrin.h>
#elif defined(__ALTIVEC__)
@@ -90,7 +94,106 @@ void convert_s16_to_float_init_simd(void)
s16_to_float_neon_enabled = true;
}
#else
void convert_s16_to_float(float *s,
/* AVX2 and AVX-512 forms of the SSE2 loop below, built per function and
* chosen by convert_s16_to_float_init_simd(), so older CPUs keep SSE2
* and compilers without per-function targets leave them out. Each
* sample is one exact widen and one multiply by gain / 32768 - the same
* single rounding as the SSE2 and scalar paths, so every target gives
* the same float for the same s16. */
#if defined(__SSE2__) && (defined(__x86_64__) || defined(__i386__) \
|| defined(_M_X64) || defined(_M_IX86))
#if defined(_MSC_VER) && !defined(__clang__)
#if _MSC_VER >= 1911
#define S16_TO_FLOAT_HAVE_WIDE 1
#define S16_TO_FLOAT_HAVE_AVX512 1
#define S16_TO_FLOAT_TARGET_AVX2
#define S16_TO_FLOAT_TARGET_AVX512
#endif
#elif defined(__clang__) && (defined(__SCE__) || defined(_MSC_VER))
/* Clang's immintrin.h for PlayStation and clang-cl only declares the
* intrinsics of ISAs enabled for the whole build, so per-function
* targets cannot reach them; build a tier only when the baseline has
* it (PS5's Zen 2 keeps AVX2, PS4's Jaguar has neither). */
#ifdef __AVX2__
#define S16_TO_FLOAT_HAVE_WIDE 1
#define S16_TO_FLOAT_TARGET_AVX2
#if defined(__AVX512F__) && defined(__AVX512BW__) && defined(__AVX512VL__)
#define S16_TO_FLOAT_HAVE_AVX512 1
#define S16_TO_FLOAT_TARGET_AVX512
#endif
#endif
#elif (defined(__clang__) && __clang_major__ >= 4) \
|| (defined(__GNUC__) && !defined(__clang__) && __GNUC__ >= 5)
#define S16_TO_FLOAT_HAVE_WIDE 1
#define S16_TO_FLOAT_HAVE_AVX512 1
#define S16_TO_FLOAT_TARGET_AVX2 __attribute__((target("avx2")))
#define S16_TO_FLOAT_TARGET_AVX512 \
__attribute__((target("avx512f,avx512bw,avx512vl")))
#endif
#endif
#ifdef S16_TO_FLOAT_HAVE_WIDE
#include <immintrin.h>
/* 0 = SSE2, 1 = AVX2, 2 = AVX-512; set once by init, before audio runs. */
static int s16_to_float_tier = 0;
#ifdef S16_TO_FLOAT_TEST_HOOKS
/* -1 leaves the tier alone; otherwise caps it for a test. */
int s16_to_float_tier_cap = -1;
#endif
#if defined(_MSC_VER) && !defined(__clang__)
#define S16_TO_FLOAT_ZEROUPPER() _mm256_zeroupper()
#else
#define S16_TO_FLOAT_ZEROUPPER()
#endif
/* Sixteen samples a step; returns how many it converted, leaving the
* rest (under 16) to the SSE2 loop and the scalar tail. */
S16_TO_FLOAT_TARGET_AVX2
static size_t s16_to_float_avx2(float *s, const int16_t *in, size_t len,
float gain)
{
size_t i;
const __m256 factor = _mm256_set1_ps(gain / 0x8000);
for (i = 0; i + 16 <= len; i += 16)
{
_mm256_storeu_ps(s + i, _mm256_mul_ps(_mm256_cvtepi32_ps(
_mm256_cvtepi16_epi32(_mm_loadu_si128(
(const __m128i*)(in + i)))), factor));
_mm256_storeu_ps(s + i + 8, _mm256_mul_ps(_mm256_cvtepi32_ps(
_mm256_cvtepi16_epi32(_mm_loadu_si128(
(const __m128i*)(in + i + 8)))), factor));
}
S16_TO_FLOAT_ZEROUPPER();
return i;
}
/* Sixteen samples a step, the last masked, so no scalar tail. */
#ifdef S16_TO_FLOAT_HAVE_AVX512
S16_TO_FLOAT_TARGET_AVX512
static void s16_to_float_avx512(float *s, const int16_t *in, size_t len,
float gain)
{
size_t i;
const __m512 factor = _mm512_set1_ps(gain / 0x8000);
for (i = 0; i < len; i += 16)
{
size_t left = len - i;
__mmask16 m = (left >= 16) ? (__mmask16)0xffff
: (__mmask16)((1u << left) - 1u);
_mm512_mask_storeu_ps(s + i, m, _mm512_mul_ps(_mm512_cvtepi32_ps(
_mm512_cvtepi16_epi32(_mm256_maskz_loadu_epi16(m,
in + i))), factor));
}
S16_TO_FLOAT_ZEROUPPER();
}
#endif
#endif
/* The SSE2 / AltiVec / Allegrex loop and the scalar tail. */
static void convert_s16_to_float_base(float *s,
const int16_t *in, size_t len, float gain)
{
unsigned i = 0;
@@ -200,6 +303,45 @@ void convert_s16_to_float(float *s,
s[i] = (float)in[i] * gain;
}
void convert_s16_to_float_init_simd(void) { }
void convert_s16_to_float(float *s,
const int16_t *in, size_t len, float gain)
{
#ifdef S16_TO_FLOAT_HAVE_WIDE
int tier = s16_to_float_tier;
#ifdef S16_TO_FLOAT_TEST_HOOKS
if (s16_to_float_tier_cap >= 0 && tier > s16_to_float_tier_cap)
tier = s16_to_float_tier_cap;
#endif
#ifdef S16_TO_FLOAT_HAVE_AVX512
if (tier == 2)
{
s16_to_float_avx512(s, in, len, gain);
return;
}
#endif
if (tier == 1)
{
size_t done = s16_to_float_avx2(s, in, len, gain);
s += done;
in += done;
len -= done;
}
#endif
convert_s16_to_float_base(s, in, len, gain);
}
void convert_s16_to_float_init_simd(void)
{
#ifdef S16_TO_FLOAT_HAVE_WIDE
uint64_t cpu = cpu_features_get();
s16_to_float_tier = 0;
if (cpu & RETRO_SIMD_AVX2)
s16_to_float_tier = 1;
#ifdef S16_TO_FLOAT_HAVE_AVX512
if (cpu & RETRO_SIMD_AVX512)
s16_to_float_tier = 2;
#endif
#endif
}
#endif
+4 -3
View File
@@ -26,6 +26,8 @@
#include <libretro_dspfilter.h>
#include "dsp_round.h"
struct bitcrusher_data
{
int32_t held_i[2];
@@ -56,8 +58,7 @@ static float bitcrusher_quantize(float x, float scale)
static int32_t bitcrusher_quantize_i16(int32_t s, int32_t scale)
{
int64_t num = (int64_t)s * scale;
int64_t q = (num >= 0) ? ((num + 16384) >> 15)
: -(((-num) + 16384) >> 15);
int64_t q = dsp_round_shift(num, 15);
int64_t y;
num = q * 32768;
@@ -103,7 +104,7 @@ static int16_t bitcrusher_mix_i16(int32_t in, int32_t held, int32_t mix_q16)
{
int64_t d = (int64_t)(held - in) * mix_q16;
int64_t v;
d = (d >= 0) ? ((d + 32768) >> 16) : -(((-d) + 32768) >> 16);
d = dsp_round_shift(d, 16);
v = in + d;
if (v > 32767)
v = 32767;
+4 -4
View File
@@ -28,6 +28,8 @@
#include <retro_math.h>
#include <libretro_dspfilter.h>
#include "dsp_round.h"
#define CHORUS_MAX_DELAY 4096
#define CHORUS_DELAY_MASK (CHORUS_MAX_DELAY - 1)
@@ -164,16 +166,14 @@ static void chorus_process_i16(void *data,
* The wet term folds the Q16 chorus and the Q16 gain: (gain*chorus)>>16. */
acc = (int64_t)ch->mix_dry_i * in0
+ ((int64_t)ch->mix_wet_i * chorus_l >> 16);
v = (acc >= 0) ? (int32_t)(( acc + 32768) >> 16)
: -(int32_t)((-acc + 32768) >> 16);
v = (int32_t)dsp_round_shift(acc, 16);
if (v > 32767) v = 32767;
else if (v < -32768) v = -32768;
out[0] = (int16_t)v;
acc = (int64_t)ch->mix_dry_i * in1
+ ((int64_t)ch->mix_wet_i * chorus_r >> 16);
v = (acc >= 0) ? (int32_t)(( acc + 32768) >> 16)
: -(int32_t)((-acc + 32768) >> 16);
v = (int32_t)dsp_round_shift(acc, 16);
if (v > 32767) v = 32767;
else if (v < -32768) v = -32768;
out[1] = (int16_t)v;
+44
View File
@@ -0,0 +1,44 @@
/* Copyright (C) 2010-2026 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (dsp_round.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef __LIBRETRO_SDK_DSP_ROUND_H
#define __LIBRETRO_SDK_DSP_ROUND_H
#include <stdint.h>
#include <retro_inline.h>
/* v / 2^s rounded to the nearest integer, halves away from zero, for
* 1 <= s <= 62 and |v| < 2^63 - 2^(s - 1): the same number as
*
* (v >= 0) ? ((v + h) >> s) : -(((-v) + h) >> s), h = 2^(s - 1)
*
* without its branch, which the int16 paths took on every sample. It
* is floor((v + h - (v < 0)) / 2^s), v >> 63 being -1 or 0. This takes
* >> of a negative value as the arithmetic shift it is on every
* compiler RetroArch is built with (GCC, Clang, MSVC and the console
* toolchains); the output is the same everywhere. */
static INLINE int64_t dsp_round_shift(int64_t v, unsigned s)
{
return (v + ((int64_t)1 << (s - 1)) + (v >> 63)) >> s;
}
#endif
+8 -8
View File
@@ -28,6 +28,8 @@
#include <retro_miscellaneous.h>
#include <libretro_dspfilter.h>
#include "dsp_round.h"
struct echo_channel
{
float *buffer;
@@ -163,9 +165,9 @@ static void echo_process_i16(void *data,
/* echo *= amp (Q16 * Q16 -> Q16). */
p = echo_l * echo->amp_q;
echo_l = (p >= 0) ? ((p + 32768) >> 16) : -(((-p) + 32768) >> 16);
echo_l = dsp_round_shift(p, 16);
p = echo_r * echo->amp_q;
echo_r = (p >= 0) ? ((p + 32768) >> 16) : -(((-p) + 32768) >> 16);
echo_r = dsp_round_shift(p, 16);
left = (int64_t)in0 * 65536 + echo_l; /* Q16 */
right = (int64_t)in1 * 65536 + echo_r;
@@ -175,8 +177,8 @@ static void echo_process_i16(void *data,
int32_t fbq = echo->channels[c].feedback_q;
int64_t fl = echo_l * fbq; /* Q32 */
int64_t fr = echo_r * fbq;
fl = (fl >= 0) ? ((fl + 32768) >> 16) : -(((-fl) + 32768) >> 16); /* Q16 */
fr = (fr >= 0) ? ((fr + 32768) >> 16) : -(((-fr) + 32768) >> 16);
fl = dsp_round_shift(fl, 16); /* Q16 */
fr = dsp_round_shift(fr, 16);
echo->channels[c].buffer_i[(echo->channels[c].ptr << 1) + 0] =
echo_sat_q16((int64_t)in0 * 65536 + fl);
@@ -187,14 +189,12 @@ static void echo_process_i16(void *data,
(echo->channels[c].ptr + 1) % echo->channels[c].frames;
}
v = (left >= 0) ? (int32_t)(( left + 32768) >> 16)
: -(int32_t)((-left + 32768) >> 16);
v = (int32_t)dsp_round_shift(left, 16);
if (v > 32767) v = 32767;
else if (v < -32768) v = -32768;
out[0] = (int16_t)v;
v = (right >= 0) ? (int32_t)(( right + 32768) >> 16)
: -(int32_t)((-right + 32768) >> 16);
v = (int32_t)dsp_round_shift(right, 16);
if (v > 32767) v = 32767;
else if (v < -32768) v = -32768;
out[1] = (int16_t)v;
+62 -22
View File
@@ -21,6 +21,7 @@
*/
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
@@ -175,11 +176,35 @@ static INLINE int64_t eq_rsh(int64_t v, unsigned s)
return (v >= 0) ? ((v + h) >> s) : -(((-v) + h) >> s);
}
static INLINE uint32_t eq_abs32(int32_t v)
/* eq_rsh(v, 30) for the butterflies' products, and eq_rsh(v, 1) for an
* int32 value, without a branch: rounding half away from zero is
* floor((v + h - (v < 0)) / 2^s) (v >> 63 being -1 or 0), and halving is v / 2 rounded down
* plus one for odd positive v. Both take >> of a negative value as the
* arithmetic shift it is on every target this builds for (as the lane
* code elsewhere does); they make the same numbers as eq_rsh. */
static INLINE int32_t eq_rsh30(int64_t v)
{
return (uint32_t)(v < 0 ? -v : v);
return (int32_t)((v + ((int64_t)1 << 29) + (v >> 63)) >> 30);
}
/* The same, spelled differently for the imaginary part, so GCC on
* AArch64 does not pair the two into a vector, moving both products
* from general to vector registers and back for one add and shift. */
static INLINE int32_t eq_rsh30_alt(int64_t v)
{
return (int32_t)((v + ((int64_t)1 << 29) - (v < 0)) >> 30);
}
static INLINE int32_t eq_half32(int32_t v)
{
return (v >> 1) + (v & 1 & (int32_t)(v >= 0));
}
/* 1 when v is outside (-2^29, 2^29), the range the butterflies keep
* every value in: |v| >= EQ_I16_LIMIT. */
#define EQ_I16_OVER(v) ((uint32_t)(v) + (uint32_t)(EQ_I16_LIMIT - 1) \
> (uint32_t)(2 * EQ_I16_LIMIT - 2))
/* In-place radix-2 transform on interleaved Q-format complex values in
* bit-reversed order; dir is -1 forward and +1 inverse, matching the
* float butterflies. *exp gains one for every halving. */
@@ -188,39 +213,54 @@ static void eq_fft_i(const struct eq_data *eq, int32_t *x, int dir, int *exp)
unsigned step, i, j;
unsigned n = eq->fft->size;
const int32_t *tw = eq->twiddle_i + 2 * (size_t)n;
uint32_t m = 0;
unsigned over = 0;
for (i = 0; i < 2 * n; i++)
m |= eq_abs32(x[i]);
over |= EQ_I16_OVER(x[i]);
for (step = 1; step < n; step <<= 1)
{
int phase_step = (int)n * dir / (int)step;
ptrdiff_t wstep = 2 * (ptrdiff_t)((int)n * dir / (int)step);
/* |a| + |w * b| stays below 2^31 while every value is below 2^29. */
if (m >= (uint32_t)EQ_I16_LIMIT)
if (over)
{
for (i = 0; i < 2 * n; i++)
x[i] = (int32_t)eq_rsh(x[i], 1);
x[i] = eq_half32(x[i]);
(*exp)++;
}
m = 0;
over = 0;
for (i = 0; i < n; i += step << 1)
{
for (j = i; j < i + step; j++)
const int32_t *w = tw + wstep;
int32_t *a = x + 2 * i;
int32_t *b = x + 2 * (i + step);
/* The first twiddle is (2^30, 0) exactly, so its products
* round back to b itself. */
{
const int32_t *w = tw + 2 * (phase_step * (int)(j - i));
int32_t *a = x + 2 * j;
int32_t *b = x + 2 * (j + step);
int32_t tr = (int32_t)eq_rsh((int64_t)w[0] * b[0]
- (int64_t)w[1] * b[1], 30);
int32_t ti = (int32_t)eq_rsh((int64_t)w[0] * b[1]
+ (int64_t)w[1] * b[0], 30);
b[0] = a[0] - tr;
b[1] = a[1] - ti;
a[0] += tr;
a[1] += ti;
m |= eq_abs32(a[0]) | eq_abs32(a[1])
| eq_abs32(b[0]) | eq_abs32(b[1]);
int32_t a0 = a[0], a1 = a[1];
int32_t b0 = b[0], b1 = b[1];
b[0] = a0 - b0;
b[1] = a1 - b1;
a[0] = a0 + b0;
a[1] = a1 + b1;
over |= EQ_I16_OVER(a[0]) | EQ_I16_OVER(a[1])
| EQ_I16_OVER(b[0]) | EQ_I16_OVER(b[1]);
}
a += 2;
b += 2;
for (j = 1; j < step; j++, w += wstep, a += 2, b += 2)
{
int32_t tr = eq_rsh30((int64_t)w[0] * b[0]
- (int64_t)w[1] * b[1]);
int32_t ti = eq_rsh30_alt((int64_t)w[0] * b[1]
+ (int64_t)w[1] * b[0]);
int32_t a0 = a[0], a1 = a[1];
b[0] = a0 - tr;
b[1] = a1 - ti;
a[0] = a0 + tr;
a[1] = a1 + ti;
over |= EQ_I16_OVER(a[0]) | EQ_I16_OVER(a[1])
| EQ_I16_OVER(b[0]) | EQ_I16_OVER(b[1]);
}
}
}
+4 -4
View File
@@ -27,6 +27,8 @@
#include <retro_inline.h>
#include <libretro_dspfilter.h>
#include "dsp_round.h"
/* int16 path: the shaper is a table of the float curve over
* [-16, 16) in Q20 input units, 128 points per unit, read with linear
* interpolation; signals run in Q28 (1.0 = full scale). */
@@ -122,12 +124,10 @@ static void overdrive_process(void *data, struct dspfilter_output *output,
overdrive_run((struct overdrive_data*)data, input->samples, input->frames);
}
/* Round-half-away-from-zero arithmetic shift, defined for negative
* values without relying on implementation-defined >> of signed. */
/* Round-half-away-from-zero arithmetic shift (see dsp_round.h). */
static INLINE int64_t overdrive_rsh(int64_t v, unsigned s)
{
int64_t h = (int64_t)1 << (s - 1);
return (v >= 0) ? ((v + h) >> s) : -(((-v) + h) >> s);
return dsp_round_shift(v, s);
}
static INLINE int32_t overdrive_shape_i(const struct overdrive_data *od,
+4 -2
View File
@@ -26,6 +26,8 @@
#include <libretro_dspfilter.h>
#include "dsp_round.h"
struct panning_data
{
float left[2];
@@ -81,8 +83,8 @@ static void panning_process_i16(void *data,
+ (int64_t)right * pan->left_i[1];
int64_t r = (int64_t)left * pan->right_i[0]
+ (int64_t)right * pan->right_i[1];
l = (l >= 0) ? ((l + 32768) >> 16) : -(((-l) + 32768) >> 16);
r = (r >= 0) ? ((r + 32768) >> 16) : -(((-r) + 32768) >> 16);
l = dsp_round_shift(l, 16);
r = dsp_round_shift(r, 16);
if (l > 32767) l = 32767;
else if (l < -32768) l = -32768;
if (r > 32767) r = 32767;
+3 -2
View File
@@ -30,6 +30,8 @@
#include <retro_math.h>
#include <libretro_dspfilter.h>
#include "dsp_round.h"
#define PHASER_LFO_SHAPE 4.0
#define PHASER_LFO_SKIP_SAMPLES 20
@@ -134,8 +136,7 @@ static void phaser_process(void *data, struct dspfilter_output *output,
static INLINE int64_t phaser_rsh(int64_t v, unsigned s)
{
int64_t h = (int64_t)1 << (s - 1);
return (v >= 0) ? ((v + h) >> s) : -(((-v) + h) >> s);
return dsp_round_shift(v, s);
}
static INLINE int32_t phaser_sat(int64_t v)
+10 -4
View File
@@ -28,6 +28,8 @@
#include <retro_inline.h>
#include <libretro_dspfilter.h>
#include "dsp_round.h"
struct comb
{
float *buffer;
@@ -98,11 +100,16 @@ static INLINE int64_t rsat_q16(int64_t v)
return v;
}
/* A Q32 product rounded to Q16, half away from zero. */
static INLINE int64_t rround_q16(int64_t p)
{
return dsp_round_shift(p, 16);
}
/* Q16 multiply with round-half-away-from-zero, for the int16 path. */
static INLINE int64_t rmul_q16(int64_t x, int32_t g)
{
int64_t p = x * (int64_t)g;
return (p >= 0) ? ((p + 32768) >> 16) : -(((-p) + 32768) >> 16);
return rround_q16(x * (int64_t)g);
}
static INLINE int64_t comb_process_i16(struct comb *c, int64_t input)
@@ -201,8 +208,7 @@ static int32_t revmodel_process_i16(struct revmodel *rev, int32_t in)
mono_out = allpass_process_i16(&rev->allpassL[i], mono_out);
res = (int64_t)in * rev->dry_q + rmul_q16(mono_out, rev->wet1_q); /* Q16 */
v = (res >= 0) ? (int32_t)(( res + 32768) >> 16)
: -(int32_t)((-res + 32768) >> 16);
v = (int32_t)rround_q16(res);
if (v > 32767) v = 32767;
else if (v < -32768) v = -32768;
return v;
+6 -8
View File
@@ -29,6 +29,8 @@
#include <retro_math.h>
#include <libretro_dspfilter.h>
#include "dsp_round.h"
#define sqr(a) ((a) * (a))
#define VIBRATO_BASE_DELAY_SEC 0.002f /* 2 ms */
@@ -69,17 +71,13 @@ static int32_t hermite_interp_i16(int32_t x, const int16_t *y)
int32_t r;
p = acc * x;
acc = ((p >= 0) ? ((p + 32768) >> 16)
: -(((-p) + 32768) >> 16)) + c2 * 65536;
acc = (dsp_round_shift(p, 16)) + c2 * 65536;
p = acc * x;
acc = ((p >= 0) ? ((p + 32768) >> 16)
: -(((-p) + 32768) >> 16)) + c1 * 65536;
acc = (dsp_round_shift(p, 16)) + c1 * 65536;
p = acc * x;
acc = ((p >= 0) ? ((p + 32768) >> 16)
: -(((-p) + 32768) >> 16)) + c0 * 65536;
acc = (dsp_round_shift(p, 16)) + c0 * 65536;
/* acc is Q16 of 2*result; result = acc / (2 * 65536) = acc / 131072. */
r = (acc >= 0) ? (int32_t)(( acc + 65536) >> 17)
: -(int32_t)((-acc + 65536) >> 17);
r = (int32_t)dsp_round_shift(acc, 17);
if (r > 32767) r = 32767;
else if (r < -32768) r = -32768;
return r;
+638 -13
View File
@@ -47,10 +47,60 @@
#include <xmmintrin.h>
#endif
#if defined(__AVX__)
/* The AVX and AVX-512 kernels are built whenever the compiler can emit
* them for a single function, whatever the build's baseline, and are
* only selected when cpu_features_get() reports the CPU and OS support
* them. A build for a baseline CPU therefore still carries them, and a
* compiler too old for per-function targets simply leaves them out.
* MSVC allows any intrinsic in any function and needs no attribute. */
#if defined(__x86_64__) || defined(__i386__) || defined(_M_X64) || defined(_M_IX86)
#if defined(_MSC_VER) && !defined(__clang__)
#if _MSC_VER >= 1600
#define SINC_HAVE_AVX 1
#define SINC_TARGET_AVX
#endif
#if _MSC_VER >= 1911
#define SINC_HAVE_AVX512 1
#define SINC_TARGET_AVX512
#endif
#elif defined(__clang__) && (defined(__SCE__) || defined(_MSC_VER))
/* Clang's immintrin.h for PlayStation and clang-cl only declares the
* intrinsics of ISAs enabled for the whole build, so per-function
* targets cannot reach them; build a tier only when the baseline has it. */
#ifdef __AVX__
#define SINC_HAVE_AVX 1
#define SINC_TARGET_AVX
#endif
#ifdef __AVX512F__
#define SINC_HAVE_AVX512 1
#define SINC_TARGET_AVX512
#endif
#elif (defined(__clang__) && __clang_major__ >= 4) \
|| (defined(__GNUC__) && !defined(__clang__) \
&& (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)))
#define SINC_HAVE_AVX 1
#define SINC_HAVE_AVX512 1
#define SINC_TARGET_AVX __attribute__((target("avx")))
#define SINC_TARGET_AVX512 __attribute__((target("avx512f")))
#elif defined(__AVX__)
#define SINC_HAVE_AVX 1
#define SINC_TARGET_AVX
#endif
#endif
#if defined(SINC_HAVE_AVX) || defined(SINC_HAVE_AVX512)
#include <immintrin.h>
#endif
/* MSVC leaves the upper YMM/ZMM halves dirty on return; clear them so
* the SSE code that follows pays no transition penalty. GCC and Clang
* insert this themselves for target functions. */
#if defined(_MSC_VER) && !defined(__clang__)
#define SINC_ZEROUPPER() _mm256_zeroupper()
#else
#define SINC_ZEROUPPER()
#endif
/* Rough SNR values for upsampling:
* LOWEST: 40 dB
* LOWER: 55 dB
@@ -306,7 +356,8 @@ static void resampler_sinc_process_neon(void *re_, struct resampler_data *data)
}
#endif
#if defined(__AVX__)
#ifdef SINC_HAVE_AVX
SINC_TARGET_AVX
static void resampler_sinc_process_avx_kaiser(void *re_, struct resampler_data *data)
{
rarch_sinc_resampler_t *resamp = (rarch_sinc_resampler_t*)re_;
@@ -373,8 +424,10 @@ static void resampler_sinc_process_avx_kaiser(void *re_, struct resampler_data *
}
data->output_frames = out_frames;
SINC_ZEROUPPER();
}
SINC_TARGET_AVX
static void resampler_sinc_process_avx(void *re_, struct resampler_data *data)
{
rarch_sinc_resampler_t *resamp = (rarch_sinc_resampler_t*)re_;
@@ -435,6 +488,159 @@ static void resampler_sinc_process_avx(void *re_, struct resampler_data *data)
}
data->output_frames = out_frames;
SINC_ZEROUPPER();
}
#endif
#ifdef SINC_HAVE_AVX512
/* Sixteen taps per step with a fused multiply-add. taps is a multiple
* of 8, so a tail of 8 may be left over; a masked load covers it with
* zeroes in the upper lanes instead of a second loop, and cannot fault
* past the end because masked-off lanes are never read. */
#define SINC_AVX512_SUM(sum) \
_mm256_add_ps(_mm512_castps512_ps256(sum), \
_mm256_castpd_ps(_mm512_extractf64x4_pd(_mm512_castps_pd(sum), 1)))
/* Both channels' 8 partial sums down to [l, r] in one register, stored
* as the output frame with a single 64-bit store. */
#define SINC_AVX512_STORE(out, res_l, res_r) \
do { \
__m128 l4_ = _mm_add_ps(_mm256_castps256_ps128(res_l), \
_mm256_extractf128_ps(res_l, 1)); \
__m128 r4_ = _mm_add_ps(_mm256_castps256_ps128(res_r), \
_mm256_extractf128_ps(res_r, 1)); \
__m128 lr_ = _mm_hadd_ps(l4_, r4_); \
lr_ = _mm_hadd_ps(lr_, lr_); \
_mm_storel_epi64((__m128i*)(out), _mm_castps_si128(lr_)); \
} while (0)
SINC_TARGET_AVX512
static void resampler_sinc_process_avx512_kaiser(void *re_, struct resampler_data *data)
{
rarch_sinc_resampler_t *resamp = (rarch_sinc_resampler_t*)re_;
unsigned phases = 1 << (resamp->phase_bits + resamp->subphase_bits);
uint32_t ratio = phases / data->ratio;
const float *input = data->data_in;
float *output = data->data_out;
size_t frames = data->input_frames;
size_t out_frames = 0;
unsigned taps = resamp->taps;
unsigned taps2 = taps * 2;
unsigned full = taps & ~15u;
__mmask16 tail = (taps & 8) ? 0x00FF : 0;
while (frames)
{
SINC_PUSH_INPUT_SAMPLES(resamp, input, taps, phases, frames);
{
const float *buffer_l = resamp->buffer_l + resamp->ptr;
const float *buffer_r = resamp->buffer_r + resamp->ptr;
while (resamp->time < phases)
{
/* C89: all declarations at top of block */
unsigned i;
__m256 res_l, res_r;
unsigned phase = resamp->time >> resamp->subphase_bits;
const float *phase_table = resamp->phase_table + phase * taps2;
const float *delta_table = phase_table + taps;
__m512 delta = _mm512_set1_ps((float)
(resamp->time & resamp->subphase_mask) * resamp->subphase_mod);
__m512 sum_l = _mm512_setzero_ps();
__m512 sum_r = _mm512_setzero_ps();
for (i = 0; i < full; i += 16)
{
__m512 sinc_v = _mm512_fmadd_ps(_mm512_loadu_ps(delta_table + i),
delta, _mm512_loadu_ps(phase_table + i));
sum_l = _mm512_fmadd_ps(_mm512_loadu_ps(buffer_l + i), sinc_v, sum_l);
sum_r = _mm512_fmadd_ps(_mm512_loadu_ps(buffer_r + i), sinc_v, sum_r);
}
if (tail)
{
__m512 sinc_v = _mm512_fmadd_ps(
_mm512_maskz_loadu_ps(tail, delta_table + i), delta,
_mm512_maskz_loadu_ps(tail, phase_table + i));
sum_l = _mm512_fmadd_ps(
_mm512_maskz_loadu_ps(tail, buffer_l + i), sinc_v, sum_l);
sum_r = _mm512_fmadd_ps(
_mm512_maskz_loadu_ps(tail, buffer_r + i), sinc_v, sum_r);
}
res_l = SINC_AVX512_SUM(sum_l);
res_r = SINC_AVX512_SUM(sum_r);
SINC_AVX512_STORE(output, res_l, res_r);
output += 2;
out_frames++;
resamp->time += ratio;
}
}
}
data->output_frames = out_frames;
SINC_ZEROUPPER();
}
SINC_TARGET_AVX512
static void resampler_sinc_process_avx512(void *re_, struct resampler_data *data)
{
rarch_sinc_resampler_t *resamp = (rarch_sinc_resampler_t*)re_;
unsigned phases = 1 << (resamp->phase_bits + resamp->subphase_bits);
uint32_t ratio = phases / data->ratio;
const float *input = data->data_in;
float *output = data->data_out;
size_t frames = data->input_frames;
size_t out_frames = 0;
unsigned taps = resamp->taps;
unsigned full = taps & ~15u;
__mmask16 tail = (taps & 8) ? 0x00FF : 0;
while (frames)
{
SINC_PUSH_INPUT_SAMPLES(resamp, input, taps, phases, frames);
{
const float *buffer_l = resamp->buffer_l + resamp->ptr;
const float *buffer_r = resamp->buffer_r + resamp->ptr;
while (resamp->time < phases)
{
/* C89: all declarations at top of block */
unsigned i;
__m256 res_l, res_r;
unsigned phase = resamp->time >> resamp->subphase_bits;
const float *phase_table = resamp->phase_table + phase * taps;
__m512 sum_l = _mm512_setzero_ps();
__m512 sum_r = _mm512_setzero_ps();
for (i = 0; i < full; i += 16)
{
__m512 sinc_v = _mm512_loadu_ps(phase_table + i);
sum_l = _mm512_fmadd_ps(_mm512_loadu_ps(buffer_l + i), sinc_v, sum_l);
sum_r = _mm512_fmadd_ps(_mm512_loadu_ps(buffer_r + i), sinc_v, sum_r);
}
if (tail)
{
__m512 sinc_v = _mm512_maskz_loadu_ps(tail, phase_table + i);
sum_l = _mm512_fmadd_ps(
_mm512_maskz_loadu_ps(tail, buffer_l + i), sinc_v, sum_l);
sum_r = _mm512_fmadd_ps(
_mm512_maskz_loadu_ps(tail, buffer_r + i), sinc_v, sum_r);
}
res_l = SINC_AVX512_SUM(sum_l);
res_r = SINC_AVX512_SUM(sum_r);
SINC_AVX512_STORE(output, res_l, res_r);
output += 2;
out_frames++;
resamp->time += ratio;
}
}
}
data->output_frames = out_frames;
SINC_ZEROUPPER();
}
#endif
@@ -590,6 +796,323 @@ static void resampler_sinc_process_sse(void *re_, struct resampler_data *data)
}
#endif
/* Largest tap count each tier reads directly; see below. */
#define SINC_IL_TAPS_SSE 16
#define SINC_IL_TAPS_AVX 128
#define SINC_IL_TAPS_AVX512 192
/* Up to a tap count (SINC_IL_TAPS_*), the x86 vector kernels read the
* input where it lies.
*
* C and NEON keep the planar rings: every input frame is split into
* buffer_l / buffer_r, each sample stored twice so a window is always
* contiguous. The kernels below skip that. A window that falls inside
* the batch is read straight out of data_in, interleaved as the core
* gave it; only the windows that reach back past the start of the batch
* read from a seam, the last 'taps' frames of history followed by the
* first 'taps - 1' frames of the batch. That is 4 * taps - 2 floats, so
* the seam lives in the 4 * taps floats the rings occupy for C and NEON,
* and everything that copies, clears or restarts the rings carries on
* unchanged. Per batch the seam costs two copies of under 'taps' frames,
* against four scalar stores per input frame for the rings.
*
* The window is read oldest frame first, the reverse of the ring, so the
* phase tables are reversed for these kernels at init, and arranged in
* blocks so one unpack against itself yields the coefficients doubled
* up for L/R pairs in exactly the order the frames are in (see
* sinc_il_layout). */
#define SINC_IL_SEAM_BEGIN(seam, input, taps, n_in) \
do { \
size_t head_ = ((n_in) < (size_t)(taps) - 1) \
? (n_in) : (size_t)(taps) - 1; \
memcpy((seam) + 2 * (taps), (input), head_ * 2 * sizeof(float)); \
} while (0)
/* History for the next batch: the last 'taps' frames of history + batch. */
#define SINC_IL_SEAM_END(seam, input, taps, n_in) \
do { \
if ((n_in) >= (size_t)(taps)) \
memcpy((seam), (input) + 2 * ((n_in) - (taps)), \
2 * (size_t)(taps) * sizeof(float)); \
else if (n_in) \
memmove((seam), (seam) + 2 * (n_in), \
2 * (size_t)(taps) * sizeof(float)); \
} while (0)
/* The control flow of SINC_PUSH_INPUT_SAMPLES without the copying:
* 'pushed' counts frames taken from the batch, and the newest window
* ends at frame pushed - 1. KERNEL(w) renders one output frame from the
* window starting at w. */
#define SINC_IL_RUN(resamp, data, KERNEL) \
rarch_sinc_resampler_t *resamp = (rarch_sinc_resampler_t*)re_; \
unsigned phases = 1 << (resamp->phase_bits + resamp->subphase_bits); \
uint32_t ratio = phases / data->ratio; \
const float *input = data->data_in; \
float *output = data->data_out; \
size_t n_in = data->input_frames; \
size_t frames = n_in; \
size_t out_frames = 0; \
size_t pushed = 0; \
unsigned taps = resamp->taps; \
float *seam = resamp->buffer_l; \
if (n_in) \
SINC_IL_SEAM_BEGIN(seam, input, taps, n_in); \
while (frames) \
{ \
const float *w; \
while (frames && resamp->time >= phases) \
{ \
pushed++; \
resamp->time -= phases; \
frames--; \
} \
w = (pushed >= taps) ? input + 2 * (pushed - taps) \
: seam + 2 * pushed; \
while (resamp->time < phases) \
{ \
KERNEL(w); \
output += 2; \
out_frames++; \
resamp->time += ratio; \
} \
} \
SINC_IL_SEAM_END(seam, input, taps, n_in); \
data->output_frames = out_frames
#ifdef SINC_HAVE_AVX
/* Eight taps a step: the table block is laid out so unpacklo/unpackhi
* give frames 0-3 and 4-7 their doubled coefficients. */
#define SINC_IL_AVX_STEP(acc0, acc1, w, i, c) \
do { \
acc0 = _mm256_add_ps(acc0, _mm256_mul_ps( \
_mm256_loadu_ps((w) + 2 * (i)), _mm256_unpacklo_ps(c, c))); \
acc1 = _mm256_add_ps(acc1, _mm256_mul_ps( \
_mm256_loadu_ps((w) + 2 * (i) + 8), _mm256_unpackhi_ps(c, c))); \
} while (0)
/* [L R L R | L R L R] down to one stored frame. */
#define SINC_IL_AVX_STORE(out, acc) \
do { \
__m128 x_ = _mm_add_ps(_mm256_castps256_ps128(acc), \
_mm256_extractf128_ps(acc, 1)); \
x_ = _mm_add_ps(x_, _mm_movehl_ps(x_, x_)); \
_mm_store_ss((out), x_); \
_mm_store_ss((out) + 1, _mm_shuffle_ps(x_, x_, 1)); \
} while (0)
#define SINC_IL_AVX_KAISER(w) \
do { \
unsigned i_; \
unsigned phase_ = resamp->time >> resamp->subphase_bits; \
const float *ph_ = resamp->phase_table + phase_ * taps * 2; \
const float *dt_ = ph_ + taps; \
__m256 d_ = _mm256_set1_ps((float) \
(resamp->time & resamp->subphase_mask) * resamp->subphase_mod); \
__m256 a0_ = _mm256_setzero_ps(); \
__m256 a1_ = _mm256_setzero_ps(); \
for (i_ = 0; i_ < taps; i_ += 8) \
{ \
__m256 c_ = _mm256_add_ps(_mm256_load_ps(ph_ + i_), \
_mm256_mul_ps(_mm256_load_ps(dt_ + i_), d_)); \
SINC_IL_AVX_STEP(a0_, a1_, w, i_, c_); \
} \
a0_ = _mm256_add_ps(a0_, a1_); \
SINC_IL_AVX_STORE(output, a0_); \
} while (0)
#define SINC_IL_AVX_PLAIN(w) \
do { \
unsigned i_; \
unsigned phase_ = resamp->time >> resamp->subphase_bits; \
const float *ph_ = resamp->phase_table + phase_ * taps; \
__m256 a0_ = _mm256_setzero_ps(); \
__m256 a1_ = _mm256_setzero_ps(); \
for (i_ = 0; i_ < taps; i_ += 8) \
{ \
__m256 c_ = _mm256_load_ps(ph_ + i_); \
SINC_IL_AVX_STEP(a0_, a1_, w, i_, c_); \
} \
a0_ = _mm256_add_ps(a0_, a1_); \
SINC_IL_AVX_STORE(output, a0_); \
} while (0)
SINC_TARGET_AVX
static void resampler_sinc_process_avx_il_kaiser(void *re_, struct resampler_data *data)
{
SINC_IL_RUN(resamp, data, SINC_IL_AVX_KAISER);
SINC_ZEROUPPER();
}
SINC_TARGET_AVX
static void resampler_sinc_process_avx_il(void *re_, struct resampler_data *data)
{
SINC_IL_RUN(resamp, data, SINC_IL_AVX_PLAIN);
SINC_ZEROUPPER();
}
#endif
#ifdef SINC_HAVE_AVX512
/* Sixteen taps a step with fused multiply-add, through the same unpack
* trick on 16-float table blocks. taps is a multiple of 8, so a block of
* 8 may be left; that block is kept in plain order and doubled with one
* permute, its loads masked to the 8 taps that exist. */
#define SINC_IL_512_STEP(acc0, acc1, w, i, c) \
do { \
acc0 = _mm512_fmadd_ps(_mm512_loadu_ps((w) + 2 * (i)), \
_mm512_unpacklo_ps(c, c), acc0); \
acc1 = _mm512_fmadd_ps(_mm512_loadu_ps((w) + 2 * (i) + 16), \
_mm512_unpackhi_ps(c, c), acc1); \
} while (0)
#define SINC_IL_512_STORE(out, acc) \
do { \
__m256 y_ = _mm256_add_ps(_mm512_castps512_ps256(acc), \
_mm256_castpd_ps(_mm512_extractf64x4_pd( \
_mm512_castps_pd(acc), 1))); \
__m128 x_ = _mm_add_ps(_mm256_castps256_ps128(y_), \
_mm256_extractf128_ps(y_, 1)); \
x_ = _mm_add_ps(x_, _mm_movehl_ps(x_, x_)); \
_mm_store_ss((out), x_); \
_mm_store_ss((out) + 1, _mm_shuffle_ps(x_, x_, 1)); \
} while (0)
#define SINC_IL_512_KAISER(w) \
do { \
unsigned i_; \
unsigned phase_ = resamp->time >> resamp->subphase_bits; \
const float *ph_ = resamp->phase_table + phase_ * taps * 2; \
const float *dt_ = ph_ + taps; \
__m512 d_ = _mm512_set1_ps((float) \
(resamp->time & resamp->subphase_mask) * resamp->subphase_mod); \
__m512 a0_ = _mm512_setzero_ps(); \
__m512 a1_ = _mm512_setzero_ps(); \
for (i_ = 0; i_ < full; i_ += 16) \
{ \
__m512 c_ = _mm512_fmadd_ps(_mm512_loadu_ps(dt_ + i_), d_, \
_mm512_loadu_ps(ph_ + i_)); \
SINC_IL_512_STEP(a0_, a1_, w, i_, c_); \
} \
if (full != taps) \
{ \
__m512 c_ = _mm512_permutexvar_ps(dup8, _mm512_fmadd_ps( \
_mm512_maskz_loadu_ps(0x00FF, dt_ + i_), d_, \
_mm512_maskz_loadu_ps(0x00FF, ph_ + i_))); \
a1_ = _mm512_fmadd_ps(_mm512_loadu_ps((w) + 2 * i_), c_, a1_); \
} \
a0_ = _mm512_add_ps(a0_, a1_); \
SINC_IL_512_STORE(output, a0_); \
} while (0)
#define SINC_IL_512_PLAIN(w) \
do { \
unsigned i_; \
unsigned phase_ = resamp->time >> resamp->subphase_bits; \
const float *ph_ = resamp->phase_table + phase_ * taps; \
__m512 a0_ = _mm512_setzero_ps(); \
__m512 a1_ = _mm512_setzero_ps(); \
for (i_ = 0; i_ < full; i_ += 16) \
{ \
__m512 c_ = _mm512_loadu_ps(ph_ + i_); \
SINC_IL_512_STEP(a0_, a1_, w, i_, c_); \
} \
if (full != taps) \
{ \
__m512 c_ = _mm512_permutexvar_ps(dup8, \
_mm512_maskz_loadu_ps(0x00FF, ph_ + i_)); \
a1_ = _mm512_fmadd_ps(_mm512_loadu_ps((w) + 2 * i_), c_, a1_); \
} \
a0_ = _mm512_add_ps(a0_, a1_); \
SINC_IL_512_STORE(output, a0_); \
} while (0)
/* Lane k of the tail takes coefficient k / 2. */
#define SINC_IL_512_DUP8 \
_mm512_set_epi32(7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0)
SINC_TARGET_AVX512
static void resampler_sinc_process_avx512_il_kaiser(void *re_, struct resampler_data *data)
{
const __m512i dup8 = SINC_IL_512_DUP8;
unsigned full = ((rarch_sinc_resampler_t*)re_)->taps & ~15u;
SINC_IL_RUN(resamp, data, SINC_IL_512_KAISER);
SINC_ZEROUPPER();
}
SINC_TARGET_AVX512
static void resampler_sinc_process_avx512_il(void *re_, struct resampler_data *data)
{
const __m512i dup8 = SINC_IL_512_DUP8;
unsigned full = ((rarch_sinc_resampler_t*)re_)->taps & ~15u;
SINC_IL_RUN(resamp, data, SINC_IL_512_PLAIN);
SINC_ZEROUPPER();
}
#endif
#if defined(__SSE__)
/* Four taps a step; the table needs no rearranging at this width, only
* reversing. */
#define SINC_IL_SSE_STEP(acc0, acc1, w, i, c) \
do { \
acc0 = _mm_add_ps(acc0, _mm_mul_ps(_mm_loadu_ps((w) + 2 * (i)), \
_mm_unpacklo_ps(c, c))); \
acc1 = _mm_add_ps(acc1, _mm_mul_ps(_mm_loadu_ps((w) + 2 * (i) + 4), \
_mm_unpackhi_ps(c, c))); \
} while (0)
#define SINC_IL_SSE_STORE(out, acc) \
do { \
__m128 x_ = _mm_add_ps(acc, _mm_movehl_ps(acc, acc)); \
_mm_store_ss((out), x_); \
_mm_store_ss((out) + 1, _mm_shuffle_ps(x_, x_, 1)); \
} while (0)
#define SINC_IL_SSE_KAISER(w) \
do { \
unsigned i_; \
unsigned phase_ = resamp->time >> resamp->subphase_bits; \
const float *ph_ = resamp->phase_table + phase_ * taps * 2; \
const float *dt_ = ph_ + taps; \
__m128 d_ = _mm_set1_ps((float) \
(resamp->time & resamp->subphase_mask) * resamp->subphase_mod); \
__m128 a0_ = _mm_setzero_ps(); \
__m128 a1_ = _mm_setzero_ps(); \
for (i_ = 0; i_ < taps; i_ += 4) \
{ \
__m128 c_ = _mm_add_ps(_mm_load_ps(ph_ + i_), \
_mm_mul_ps(_mm_load_ps(dt_ + i_), d_)); \
SINC_IL_SSE_STEP(a0_, a1_, w, i_, c_); \
} \
a0_ = _mm_add_ps(a0_, a1_); \
SINC_IL_SSE_STORE(output, a0_); \
} while (0)
#define SINC_IL_SSE_PLAIN(w) \
do { \
unsigned i_; \
unsigned phase_ = resamp->time >> resamp->subphase_bits; \
const float *ph_ = resamp->phase_table + phase_ * taps; \
__m128 a0_ = _mm_setzero_ps(); \
__m128 a1_ = _mm_setzero_ps(); \
for (i_ = 0; i_ < taps; i_ += 4) \
{ \
__m128 c_ = _mm_load_ps(ph_ + i_); \
SINC_IL_SSE_STEP(a0_, a1_, w, i_, c_); \
} \
a0_ = _mm_add_ps(a0_, a1_); \
SINC_IL_SSE_STORE(output, a0_); \
} while (0)
static void resampler_sinc_process_sse_il_kaiser(void *re_, struct resampler_data *data)
{
SINC_IL_RUN(resamp, data, SINC_IL_SSE_KAISER);
}
static void resampler_sinc_process_sse_il(void *re_, struct resampler_data *data)
{
SINC_IL_RUN(resamp, data, SINC_IL_SSE_PLAIN);
}
#endif
static void resampler_sinc_process_c_kaiser(void *re_, struct resampler_data *data)
{
rarch_sinc_resampler_t *resamp = (rarch_sinc_resampler_t*)re_;
@@ -1113,24 +1636,52 @@ static void sinc_init_table_lanczos(
}
}
/* Widest kernel both compiled in and allowed by the mask. */
/* Widest kernel both compiled in and allowed by the mask; on x86, the
* direct-read form of it up to that tier's crossover tap count. */
static sinc_process_t sinc_select_process(int kaiser,
resampler_simd_mask_t mask, unsigned enable_avx)
resampler_simd_mask_t mask, unsigned enable_avx, unsigned taps)
{
sinc_process_t process = kaiser
? resampler_sinc_process_c_kaiser : resampler_sinc_process_c;
#if defined(__SSE__)
if (mask & RESAMPLER_SIMD_SSE)
process = kaiser
? resampler_sinc_process_sse_kaiser : resampler_sinc_process_sse;
{
if (taps <= SINC_IL_TAPS_SSE)
process = kaiser
? resampler_sinc_process_sse_il_kaiser
: resampler_sinc_process_sse_il;
else
process = kaiser
? resampler_sinc_process_sse_kaiser : resampler_sinc_process_sse;
}
#endif
#if defined(__AVX__)
#ifdef SINC_HAVE_AVX
if ((mask & RESAMPLER_SIMD_AVX) && enable_avx)
process = kaiser
? resampler_sinc_process_avx_kaiser : resampler_sinc_process_avx;
#else
(void)enable_avx;
{
if (taps <= SINC_IL_TAPS_AVX)
process = kaiser
? resampler_sinc_process_avx_il_kaiser
: resampler_sinc_process_avx_il;
else
process = kaiser
? resampler_sinc_process_avx_kaiser : resampler_sinc_process_avx;
}
#endif
#ifdef SINC_HAVE_AVX512
if ((mask & RESAMPLER_SIMD_AVX512) && enable_avx)
{
if (taps <= SINC_IL_TAPS_AVX512)
process = kaiser
? resampler_sinc_process_avx512_il_kaiser
: resampler_sinc_process_avx512_il;
else
process = kaiser
? resampler_sinc_process_avx512_kaiser
: resampler_sinc_process_avx512;
}
#endif
(void)enable_avx;
(void)taps;
#if (defined(__ARM_NEON) || defined(__ARM_NEON__) || defined(HAVE_NEON))
if (mask & RESAMPLER_SIMD_NEON)
process = kaiser
@@ -1139,6 +1690,72 @@ static sinc_process_t sinc_select_process(int kaiser,
return process;
}
/* Table block width the kernel reads with, or 0 for the kernels that
* keep the planar rings (C, NEON) and take the table as built. */
static unsigned sinc_il_width(sinc_process_t process)
{
#if defined(__SSE__)
if ( process == resampler_sinc_process_sse_il
|| process == resampler_sinc_process_sse_il_kaiser)
return 4;
#endif
#ifdef SINC_HAVE_AVX
if ( process == resampler_sinc_process_avx_il
|| process == resampler_sinc_process_avx_il_kaiser)
return 8;
#endif
#ifdef SINC_HAVE_AVX512
if ( process == resampler_sinc_process_avx512_il
|| process == resampler_sinc_process_avx512_il_kaiser)
return 16;
#endif
(void)process;
return 0;
}
/* Rearranges 'rows' rows of 'taps' coefficients for an interleaved kernel
* of the given width: each row reversed, so the first coefficient meets
* the oldest frame of the window, then each block of 'width' so that an
* unpacklo/unpackhi of the block with itself gives the first and second
* halves of its coefficients doubled up (c0 c0 c1 c1 ...) lane by lane.
* Within a block, 128-bit lane k holds c[2k], c[2k+1], c[w/2+2k],
* c[w/2+2k+1]. A trailing block of 8 at width 16 stays in plain order;
* the AVX-512 kernels double it with a permute. Kaiser coefficient and
* delta rows go through alike, so the per-element interpolation still
* pairs them. Returns false only when the scratch row cannot be had. */
static bool sinc_il_layout(float *table, unsigned rows, unsigned taps,
unsigned width)
{
unsigned r, m, b, k;
float *rev;
if (!width)
return true;
if (!(rev = (float*)malloc(taps * sizeof(float))))
return false;
for (r = 0; r < rows; r++)
{
float *row = table + (size_t)r * taps;
for (m = 0; m < taps; m++)
rev[m] = row[taps - 1 - m];
memcpy(row, rev, taps * sizeof(float));
if (width == 4)
continue;
for (b = 0; b + width <= taps; b += width)
{
unsigned half = width / 2;
for (k = 0; k < width / 4; k++)
{
row[b + 4 * k + 0] = rev[b + 2 * k];
row[b + 4 * k + 1] = rev[b + 2 * k + 1];
row[b + 4 * k + 2] = rev[b + half + 2 * k];
row[b + 4 * k + 3] = rev[b + half + 2 * k + 1];
}
}
}
free(rev);
return true;
}
void *sinc_resampler_init_hq(double bandwidth_mod,
enum resampler_quality quality, resampler_simd_mask_t mask,
int hq_oversampling)
@@ -1287,7 +1904,8 @@ void *sinc_resampler_init_hq(double bandwidth_mod,
stage->subphase_mask = 1;
stage->subphase_mod = 0.5f;
stage->kaiser_beta = dec_beta;
stage->process = sinc_select_process(0, mask, enable_avx);
stage->process = sinc_select_process(0, mask, enable_avx,
dec_taps);
}
sinc_init_table_kaiser(&re->dec[0], 0.5 * SINC_DEC_ENGAGE * dec_cutoff,
re->dec[0].phase_table, 1, dec_taps, 0);
@@ -1307,7 +1925,14 @@ void *sinc_resampler_init_hq(double bandwidth_mod,
}
re->process = sinc_select_process(window_type == SINC_WINDOW_KAISER,
mask, enable_avx);
mask, enable_avx, re->taps);
if ( !sinc_il_layout(re->phase_table,
(window_type == SINC_WINDOW_KAISER ? 2u : 1u)
<< re->phase_bits,
re->taps, sinc_il_width(re->process))
|| !sinc_il_layout(re->dec[0].phase_table, 1, dec_taps,
sinc_il_width(re->dec[0].process)))
goto error;
sinc_bind_state(re);
return re;
+702 -3
View File
@@ -69,6 +69,11 @@
#endif
#endif
/* A statement for each of eight, four or one interleaved chains */
#define AES_CBC_EACH8(S) S(0) S(1) S(2) S(3) S(4) S(5) S(6) S(7)
#define AES_CBC_EACH4(S) S(0) S(1) S(2) S(3)
#define AES_CBC_EACH1(S) S(0)
int crypto_memeq_ct(const void *a, const void *b, size_t len)
{
const volatile uint8_t *pa = (const volatile uint8_t*)a;
@@ -421,9 +426,8 @@ static void aes_x86_encrypt_block(const struct aes_ctx *ctx,
}
/* Inverse cipher on the encryption schedule: aesdec wants the round
* keys through InvMixColumns, applied here per block, as decryption
* only serves the short Kerberos messages and keeps the context and
* every other caller unchanged. */
* keys through InvMixColumns, applied here per block; a run of blocks
* goes through aes_x86_cbc_decrypt(), which makes them once. */
AES_TARGET_X86
static void aes_x86_decrypt_block(const struct aes_ctx *ctx,
const uint8_t *in, uint8_t *out)
@@ -438,6 +442,138 @@ static void aes_x86_decrypt_block(const struct aes_ctx *ctx,
_mm_storeu_si128((__m128i*)out, b);
}
/* CBC chains interleaved so the AES unit's latency on each is hidden
* behind the others, the round keys loaded and byte-swapped once per
* call. Eight chains, four or one, each in a register of its own, so
* no round waits on a store and a load besides the cipher. */
#define AES_X86_CBC(name, EACH) \
AES_TARGET_X86 \
static void name(const __m128i *k, unsigned rounds, uint8_t *const *iv, \
const uint8_t *const *in, uint8_t *const *out, size_t len) \
{ \
__m128i x0, x1, x2, x3, x4, x5, x6, x7; \
size_t off; \
unsigned r; \
EACH(AES_X86_CBC_LOAD) \
for (off = 0; off < len; off += 16) \
{ \
EACH(AES_X86_CBC_IN) \
for (r = 1; r < rounds; r++) \
{ \
const __m128i kr = k[r]; \
EACH(AES_X86_CBC_ROUND) \
} \
{ \
const __m128i kr = k[rounds]; \
EACH(AES_X86_CBC_LAST) \
} \
} \
EACH(AES_X86_CBC_STORE) \
(void)x0; (void)x1; (void)x2; (void)x3; \
(void)x4; (void)x5; (void)x6; (void)x7; \
}
#define AES_X86_CBC_LOAD(l) x##l = _mm_loadu_si128((const __m128i*)iv[l]);
#define AES_X86_CBC_IN(l) x##l = _mm_xor_si128(_mm_xor_si128(x##l, \
_mm_loadu_si128((const __m128i*)(in[l] + off))), k[0]);
#define AES_X86_CBC_ROUND(l) x##l = _mm_aesenc_si128(x##l, kr);
#define AES_X86_CBC_LAST(l) x##l = _mm_aesenclast_si128(x##l, kr); \
_mm_storeu_si128((__m128i*)(out[l] + off), x##l);
#define AES_X86_CBC_STORE(l) _mm_storeu_si128((__m128i*)iv[l], x##l);
AES_X86_CBC(aes_x86_cbc8, AES_CBC_EACH8)
AES_X86_CBC(aes_x86_cbc4, AES_CBC_EACH4)
AES_X86_CBC(aes_x86_cbc1, AES_CBC_EACH1)
AES_TARGET_X86
static void aes_x86_cbc_lanes(const struct aes_ctx *ctx, unsigned n,
uint8_t *const *iv, const uint8_t *const *in, uint8_t *const *out,
size_t len)
{
__m128i k[15];
unsigned r, rounds = ctx->rounds;
for (r = 0; r <= rounds; r++)
k[r] = aes_x86_bswap32(_mm_loadu_si128((const __m128i*)(ctx->rk + 4 * r)));
for (; n >= 8; n -= 8, iv += 8, in += 8, out += 8)
aes_x86_cbc8(k, rounds, iv, in, out, len);
for (; n >= 4; n -= 4, iv += 4, in += 4, out += 4)
aes_x86_cbc4(k, rounds, iv, in, out, len);
for (; n; n--, iv++, in++, out++)
aes_x86_cbc1(k, rounds, iv, in, out, len);
}
/* CBC decryption has no chain to wait on: eight blocks go through the
* inverse cipher at once, each XORed with the ciphertext before it.
* The InvMixColumns round keys are made once per call. Every block's
* ciphertext is held in a register before its plaintext is stored, so
* @in and @out may alias. */
AES_TARGET_X86
static void aes_x86_cbc_decrypt(const struct aes_ctx *ctx, uint8_t *iv,
const uint8_t *in, uint8_t *out, size_t len)
{
__m128i k[15];
__m128i prev = _mm_loadu_si128((const __m128i*)iv);
unsigned r, nr = ctx->rounds;
k[0] = aes_x86_bswap32(_mm_loadu_si128((const __m128i*)ctx->rk));
k[nr] = aes_x86_bswap32(_mm_loadu_si128((const __m128i*)(ctx->rk + 4 * nr)));
for (r = 1; r < nr; r++)
k[r] = _mm_aesimc_si128(aes_x86_bswap32(
_mm_loadu_si128((const __m128i*)(ctx->rk + 4 * r))));
while (len >= 128)
{
__m128i c0 = _mm_loadu_si128((const __m128i*)(in));
__m128i c1 = _mm_loadu_si128((const __m128i*)(in + 16));
__m128i c2 = _mm_loadu_si128((const __m128i*)(in + 32));
__m128i c3 = _mm_loadu_si128((const __m128i*)(in + 48));
__m128i c4 = _mm_loadu_si128((const __m128i*)(in + 64));
__m128i c5 = _mm_loadu_si128((const __m128i*)(in + 80));
__m128i c6 = _mm_loadu_si128((const __m128i*)(in + 96));
__m128i c7 = _mm_loadu_si128((const __m128i*)(in + 112));
__m128i b0 = _mm_xor_si128(c0, k[nr]);
__m128i b1 = _mm_xor_si128(c1, k[nr]);
__m128i b2 = _mm_xor_si128(c2, k[nr]);
__m128i b3 = _mm_xor_si128(c3, k[nr]);
__m128i b4 = _mm_xor_si128(c4, k[nr]);
__m128i b5 = _mm_xor_si128(c5, k[nr]);
__m128i b6 = _mm_xor_si128(c6, k[nr]);
__m128i b7 = _mm_xor_si128(c7, k[nr]);
for (r = nr - 1; r > 0; r--)
{
__m128i kr = k[r];
b0 = _mm_aesdec_si128(b0, kr); b1 = _mm_aesdec_si128(b1, kr);
b2 = _mm_aesdec_si128(b2, kr); b3 = _mm_aesdec_si128(b3, kr);
b4 = _mm_aesdec_si128(b4, kr); b5 = _mm_aesdec_si128(b5, kr);
b6 = _mm_aesdec_si128(b6, kr); b7 = _mm_aesdec_si128(b7, kr);
}
_mm_storeu_si128((__m128i*)(out), _mm_xor_si128(_mm_aesdeclast_si128(b0, k[0]), prev));
_mm_storeu_si128((__m128i*)(out + 16), _mm_xor_si128(_mm_aesdeclast_si128(b1, k[0]), c0));
_mm_storeu_si128((__m128i*)(out + 32), _mm_xor_si128(_mm_aesdeclast_si128(b2, k[0]), c1));
_mm_storeu_si128((__m128i*)(out + 48), _mm_xor_si128(_mm_aesdeclast_si128(b3, k[0]), c2));
_mm_storeu_si128((__m128i*)(out + 64), _mm_xor_si128(_mm_aesdeclast_si128(b4, k[0]), c3));
_mm_storeu_si128((__m128i*)(out + 80), _mm_xor_si128(_mm_aesdeclast_si128(b5, k[0]), c4));
_mm_storeu_si128((__m128i*)(out + 96), _mm_xor_si128(_mm_aesdeclast_si128(b6, k[0]), c5));
_mm_storeu_si128((__m128i*)(out + 112), _mm_xor_si128(_mm_aesdeclast_si128(b7, k[0]), c6));
prev = c7;
in += 128;
out += 128;
len -= 128;
}
for (; len >= 16; in += 16, out += 16, len -= 16)
{
__m128i c = _mm_loadu_si128((const __m128i*)in);
__m128i b = _mm_xor_si128(c, k[nr]);
for (r = nr - 1; r > 0; r--)
b = _mm_aesdec_si128(b, k[r]);
_mm_storeu_si128((__m128i*)out,
_mm_xor_si128(_mm_aesdeclast_si128(b, k[0]), prev));
prev = c;
}
_mm_storeu_si128((__m128i*)iv, prev);
}
/* CTR with eight blocks in flight so the AES unit's latency is hidden,
* the round keys loaded and byte-swapped once per call rather than once
* per round. The counter block is kept byte-reversed, where its 32-bit
@@ -707,6 +843,130 @@ static void aes_arm_decrypt_block(const struct aes_ctx *ctx,
vst1q_u8(out, veorq_u8(b, aes_arm_rk(ctx, 0)));
}
/* CBC chains interleaved, each in a register of its own, as the x86
* form above; the round keys loaded once. */
#define AES_ARM_CBC(name, EACH) \
AES_TARGET_ARM \
static void name(const uint8x16_t *k, unsigned rounds, uint8_t *const *iv, \
const uint8_t *const *in, uint8_t *const *out, size_t len) \
{ \
uint8x16_t x0, x1, x2, x3, x4, x5, x6, x7; \
size_t off; \
unsigned r; \
EACH(AES_ARM_CBC_LOAD) \
for (off = 0; off < len; off += 16) \
{ \
EACH(AES_ARM_CBC_IN) \
for (r = 0; r + 1 < rounds; r++) \
{ \
const uint8x16_t kr = k[r]; \
EACH(AES_ARM_CBC_ROUND) \
} \
{ \
const uint8x16_t kr = k[r], kl = k[r + 1]; \
EACH(AES_ARM_CBC_LAST) \
} \
} \
EACH(AES_ARM_CBC_STORE) \
(void)x0; (void)x1; (void)x2; (void)x3; \
(void)x4; (void)x5; (void)x6; (void)x7; \
}
#define AES_ARM_CBC_LOAD(l) x##l = vld1q_u8(iv[l]);
#define AES_ARM_CBC_IN(l) x##l = veorq_u8(x##l, vld1q_u8(in[l] + off));
#define AES_ARM_CBC_ROUND(l) x##l = vaesmcq_u8(vaeseq_u8(x##l, kr));
#define AES_ARM_CBC_LAST(l) x##l = veorq_u8(vaeseq_u8(x##l, kr), kl); \
vst1q_u8(out[l] + off, x##l);
#define AES_ARM_CBC_STORE(l) vst1q_u8(iv[l], x##l);
AES_ARM_CBC(aes_arm_cbc8, AES_CBC_EACH8)
AES_ARM_CBC(aes_arm_cbc4, AES_CBC_EACH4)
AES_ARM_CBC(aes_arm_cbc1, AES_CBC_EACH1)
AES_TARGET_ARM
static void aes_arm_cbc_lanes(const struct aes_ctx *ctx, unsigned n,
uint8_t *const *iv, const uint8_t *const *in, uint8_t *const *out,
size_t len)
{
uint8x16_t k[15];
unsigned r, rounds = ctx->rounds;
for (r = 0; r <= rounds; r++)
k[r] = aes_arm_rk(ctx, r);
for (; n >= 8; n -= 8, iv += 8, in += 8, out += 8)
aes_arm_cbc8(k, rounds, iv, in, out, len);
for (; n >= 4; n -= 4, iv += 4, in += 4, out += 4)
aes_arm_cbc4(k, rounds, iv, in, out, len);
for (; n; n--, iv++, in++, out++)
aes_arm_cbc1(k, rounds, iv, in, out, len);
}
/* CBC decryption, eight blocks at once as the x86 form above. AESD
* takes the round key before the inverse substitution, so a round is
* AESIMC on the state, then AESD with the round key through AESIMC. */
AES_TARGET_ARM
static void aes_arm_cbc_decrypt(const struct aes_ctx *ctx, uint8_t *iv,
const uint8_t *in, uint8_t *out, size_t len)
{
uint8x16_t k[15];
uint8x16_t prev = vld1q_u8(iv);
unsigned r, nr = ctx->rounds;
k[0] = aes_arm_rk(ctx, 0);
k[nr] = aes_arm_rk(ctx, nr);
for (r = 1; r < nr; r++)
k[r] = vaesimcq_u8(aes_arm_rk(ctx, r));
while (len >= 128)
{
uint8x16_t c0 = vld1q_u8(in);
uint8x16_t c1 = vld1q_u8(in + 16);
uint8x16_t c2 = vld1q_u8(in + 32);
uint8x16_t c3 = vld1q_u8(in + 48);
uint8x16_t c4 = vld1q_u8(in + 64);
uint8x16_t c5 = vld1q_u8(in + 80);
uint8x16_t c6 = vld1q_u8(in + 96);
uint8x16_t c7 = vld1q_u8(in + 112);
uint8x16_t b0 = vaesdq_u8(c0, k[nr]);
uint8x16_t b1 = vaesdq_u8(c1, k[nr]);
uint8x16_t b2 = vaesdq_u8(c2, k[nr]);
uint8x16_t b3 = vaesdq_u8(c3, k[nr]);
uint8x16_t b4 = vaesdq_u8(c4, k[nr]);
uint8x16_t b5 = vaesdq_u8(c5, k[nr]);
uint8x16_t b6 = vaesdq_u8(c6, k[nr]);
uint8x16_t b7 = vaesdq_u8(c7, k[nr]);
for (r = nr - 1; r > 0; r--)
{
const uint8x16_t kr = k[r];
b0 = vaesdq_u8(vaesimcq_u8(b0), kr); b1 = vaesdq_u8(vaesimcq_u8(b1), kr);
b2 = vaesdq_u8(vaesimcq_u8(b2), kr); b3 = vaesdq_u8(vaesimcq_u8(b3), kr);
b4 = vaesdq_u8(vaesimcq_u8(b4), kr); b5 = vaesdq_u8(vaesimcq_u8(b5), kr);
b6 = vaesdq_u8(vaesimcq_u8(b6), kr); b7 = vaesdq_u8(vaesimcq_u8(b7), kr);
}
vst1q_u8(out, veorq_u8(veorq_u8(b0, k[0]), prev));
vst1q_u8(out + 16, veorq_u8(veorq_u8(b1, k[0]), c0));
vst1q_u8(out + 32, veorq_u8(veorq_u8(b2, k[0]), c1));
vst1q_u8(out + 48, veorq_u8(veorq_u8(b3, k[0]), c2));
vst1q_u8(out + 64, veorq_u8(veorq_u8(b4, k[0]), c3));
vst1q_u8(out + 80, veorq_u8(veorq_u8(b5, k[0]), c4));
vst1q_u8(out + 96, veorq_u8(veorq_u8(b6, k[0]), c5));
vst1q_u8(out + 112, veorq_u8(veorq_u8(b7, k[0]), c6));
prev = c7;
in += 128;
out += 128;
len -= 128;
}
for (; len >= 16; in += 16, out += 16, len -= 16)
{
uint8x16_t c = vld1q_u8(in);
uint8x16_t b = vaesdq_u8(c, k[nr]);
for (r = nr - 1; r > 0; r--)
b = vaesdq_u8(vaesimcq_u8(b), k[r]);
vst1q_u8(out, veorq_u8(veorq_u8(b, k[0]), prev));
prev = c;
}
vst1q_u8(iv, prev);
}
/* CTR with eight blocks in flight, the round keys loaded once per
* call. The counter block is kept byte-reversed within each 32-bit
* lane, where its big-endian counter is lane 3 as a native word: one
@@ -1181,6 +1441,80 @@ void aes_ctr_crypt(const struct aes_ctx *ctx, uint8_t *counter,
crypto_memzero(ks, sizeof(ks));
}
void aes_cbc_encrypt(const struct aes_ctx *ctx, uint8_t *iv,
const uint8_t *in, uint8_t *out, size_t len)
{
aes_cbc_encrypt_lanes(ctx, 1, &iv, &in, &out, len);
}
void aes_cbc_encrypt_lanes(const struct aes_ctx *ctx, unsigned n,
uint8_t *const *iv, const uint8_t *const *in, uint8_t *const *out,
size_t len)
{
unsigned l;
#if defined(AES_HAVE_X86_PATH)
if (aes_x86_available())
{
aes_x86_cbc_lanes(ctx, n, iv, in, out, len);
return;
}
#elif defined(AES_HAVE_ARM_PATH)
if (aes_arm_available())
{
aes_arm_cbc_lanes(ctx, n, iv, in, out, len);
return;
}
#endif
for (l = 0; l < n; l++)
{
size_t off;
unsigned i;
uint8_t x[16];
memcpy(x, iv[l], 16);
for (off = 0; off < len; off += 16)
{
for (i = 0; i < 16; i++)
x[i] ^= in[l][off + i];
aes_encrypt_block(ctx, x, x);
memcpy(out[l] + off, x, 16);
}
memcpy(iv[l], x, 16);
}
}
void aes_cbc_decrypt(const struct aes_ctx *ctx, uint8_t *iv,
const uint8_t *in, uint8_t *out, size_t len)
{
uint8_t prev[16], c[16];
size_t off;
unsigned i;
#if defined(AES_HAVE_X86_PATH)
if (aes_x86_available())
{
aes_x86_cbc_decrypt(ctx, iv, in, out, len);
return;
}
#elif defined(AES_HAVE_ARM_PATH)
if (aes_arm_available())
{
aes_arm_cbc_decrypt(ctx, iv, in, out, len);
return;
}
#endif
memcpy(prev, iv, 16);
for (off = 0; off + 16 <= len; off += 16)
{
memcpy(c, in + off, 16);
aes_decrypt_block_c(ctx, c, out + off);
for (i = 0; i < 16; i++)
out[off + i] ^= prev[i];
memcpy(prev, c, 16);
}
memcpy(iv, prev, 16);
}
/* Double in GF(2^128) with the CMAC polynomial. */
static void aes_cmac_dbl(uint8_t *b)
{
@@ -1197,6 +1531,78 @@ static void aes_cmac_dbl(uint8_t *b)
b[15] ^= (uint8_t)(0x87 & (0 - msb));
}
void aes_cmac_init(struct aes_cmac_ctx *s, const struct aes_ctx *aes)
{
s->aes = aes;
s->n = 0;
memset(s->x, 0, 16);
}
/* The last block takes a subkey, so a full block is held back until
* more of the message shows it was not the last. */
void aes_cmac_update(struct aes_cmac_ctx *s, const uint8_t *msg, size_t len)
{
unsigned i;
/* the chain in a local for the call: through @s, every octet of it
* may alias @msg and the block XOR is not vectorised */
uint8_t x[16];
if (!len)
return;
memcpy(x, s->x, 16);
if (s->n)
{
size_t take = 16 - s->n;
if (take > len)
take = len;
memcpy(s->buf + s->n, msg, take);
s->n += take;
msg += take;
len -= take;
if (!len)
return;
for (i = 0; i < 16; i++)
x[i] ^= s->buf[i];
aes_encrypt_block(s->aes, x, x);
s->n = 0;
}
while (len > 16)
{
for (i = 0; i < 16; i++)
x[i] ^= msg[i];
aes_encrypt_block(s->aes, x, x);
msg += 16;
len -= 16;
}
memcpy(s->buf, msg, len);
s->n = len;
memcpy(s->x, x, 16);
crypto_memzero(x, sizeof(x));
}
void aes_cmac_final(struct aes_cmac_ctx *s, uint8_t *mac)
{
unsigned i;
uint8_t k[16];
memset(k, 0, 16);
aes_encrypt_block(s->aes, k, k);
aes_cmac_dbl(k);
if (s->n != 16)
{
/* Partial or empty last block: pad 10* and use K2 = dbl(K1). */
aes_cmac_dbl(k);
memset(s->buf + s->n, 0, 16 - s->n);
s->buf[s->n] = 0x80;
}
for (i = 0; i < 16; i++)
s->x[i] ^= s->buf[i] ^ k[i];
aes_encrypt_block(s->aes, s->x, mac);
crypto_memzero(k, sizeof(k));
crypto_memzero(s, sizeof(*s));
}
void aes_cmac(const struct aes_ctx *ctx,
const uint8_t *msg, size_t len, uint8_t *mac)
{
@@ -1247,6 +1653,187 @@ void aes_cmac(const struct aes_ctx *ctx,
/* ---- CCM (RFC 3610) ---------------------------------------------- */
/* CBC-MAC over B_0 || AAD (length-prefixed, padded) || payload
* (padded), then CTR with A_i counters; the tag is the MAC under
* counter zero. */
int aes_ccm_stream_init(struct aes_ccm_stream *s, const struct aes_ctx *ctx,
const uint8_t *nonce, size_t nonce_len,
const uint8_t *aad, size_t aad_len,
size_t msg_len, size_t tag_len)
{
size_t L, i, off;
size_t remaining;
uint8_t *x = s->x;
if (nonce_len < 7 || nonce_len > 13 || tag_len < 4 || tag_len > 16 || (tag_len & 1))
return -1;
L = 15 - nonce_len;
/* the length must fit L octets; shifted as 64 bits, since a 32-bit
* size_t shifted by 32 (L = 4, the 11-octet nonce SMB uses) is
* undefined and comes back unshifted on MIPS */
if (L < 8 && ((uint64_t)msg_len >> (8 * L)) != 0)
return -1;
s->aes = ctx;
s->len = msg_len;
s->off = 0;
s->L = L;
s->tag_len = tag_len;
/* B_0: flags | nonce | length of the message */
x[0] = (uint8_t)((aad_len ? 0x40 : 0) | (((tag_len - 2) / 2) << 3) | (L - 1));
memcpy(x + 1, nonce, nonce_len);
remaining = msg_len;
for (i = 0; i < L; i++)
{
x[15 - i] = (uint8_t)remaining;
remaining >>= 8;
}
aes_encrypt_block(ctx, x, x);
/* AAD, length-prefixed (2 or 6 octets here; > 2^32 is not needed) */
if (aad_len)
{
uint8_t blk[16];
size_t n;
memset(blk, 0, 16);
if (aad_len < 0xff00)
{
blk[0] = (uint8_t)(aad_len >> 8);
blk[1] = (uint8_t)aad_len;
off = 2;
}
else
{
blk[0] = 0xff; blk[1] = 0xfe;
crypto_store32_be(blk + 2, (uint32_t)aad_len);
off = 6;
}
n = 16 - off;
if (n > aad_len)
n = aad_len;
memcpy(blk + off, aad, n);
for (i = 0; i < 16; i++)
x[i] ^= blk[i];
aes_encrypt_block(ctx, x, x);
aad += n;
aad_len -= n;
while (aad_len)
{
n = aad_len < 16 ? aad_len : 16;
for (i = 0; i < n; i++)
x[i] ^= aad[i];
aes_encrypt_block(ctx, x, x);
aad += n;
aad_len -= n;
}
}
/* Counter blocks: flags = L - 1, nonce, counter */
memset(s->ctr, 0, 16);
s->ctr[0] = (uint8_t)(L - 1);
memcpy(s->ctr + 1, nonce, nonce_len);
aes_encrypt_block(ctx, s->ctr, s->s0); /* S_0, for the tag */
return 0;
}
/* A piece that starts or ends inside a block carries its keystream
* and the MAC chain across in @s; the chain is run when the block
* fills or the message ends. The chain, counter and keystream are
* locals for the call - through @s every octet may alias the data and
* the block loops are not vectorised. */
static void aes_ccm_stream_run(struct aes_ccm_stream *s, int encrypt,
const uint8_t *in, uint8_t *out, size_t len)
{
uint8_t x[16], ctr[16], ks[16];
size_t i, L = s->L, off = s->off;
if (len > s->len - off)
len = s->len - off;
if (!len)
return;
memcpy(x, s->x, 16);
memcpy(ctr, s->ctr, 16);
memcpy(ks, s->ks, 16);
while (len)
{
size_t pos = off & 15;
size_t n = 16 - pos;
if (n > len)
n = len;
if (!pos)
{
size_t c = off / 16 + 1;
for (i = 0; i < L; i++)
{
ctr[15 - i] = (uint8_t)c;
c >>= 8;
}
aes_encrypt_block(s->aes, ctr, ks);
}
if (n == 16)
{
if (encrypt)
{
for (i = 0; i < 16; i++)
x[i] ^= in[i];
for (i = 0; i < 16; i++)
out[i] = in[i] ^ ks[i];
}
else
for (i = 0; i < 16; i++)
{
uint8_t p = (uint8_t)(in[i] ^ ks[i]);
out[i] = p;
x[i] ^= p;
}
}
else if (encrypt)
for (i = 0; i < n; i++)
{
x[pos + i] ^= in[i];
out[i] = in[i] ^ ks[pos + i];
}
else
for (i = 0; i < n; i++)
{
out[i] = in[i] ^ ks[pos + i];
x[pos + i] ^= out[i];
}
off += n;
if (!(off & 15) || off == s->len)
aes_encrypt_block(s->aes, x, x);
in += n;
out += n;
len -= n;
}
s->off = off;
memcpy(s->x, x, 16);
memcpy(s->ctr, ctr, 16);
memcpy(s->ks, ks, 16);
crypto_memzero(x, sizeof(x));
crypto_memzero(ks, sizeof(ks));
}
void aes_ccm_stream_encrypt(struct aes_ccm_stream *s,
const uint8_t *pt, uint8_t *ct, size_t len)
{
aes_ccm_stream_run(s, 1, pt, ct, len);
}
void aes_ccm_stream_decrypt(struct aes_ccm_stream *s,
const uint8_t *ct, uint8_t *pt, size_t len)
{
aes_ccm_stream_run(s, 0, ct, pt, len);
}
void aes_ccm_stream_tag(struct aes_ccm_stream *s, uint8_t *tag)
{
size_t i;
for (i = 0; i < s->tag_len; i++)
tag[i] = s->x[i] ^ s->s0[i];
crypto_memzero(s, sizeof(*s));
}
/* CBC-MAC over B_0 || AAD (length-prefixed, padded) || payload
* (padded), then CTR with A_i counters; the tag is the MAC under
* counter zero. */
@@ -1605,6 +2192,118 @@ int aes_gcm_decrypt(const struct aes_gcm_ctx *ctx,
return 0;
}
void aes_gcm_stream_init(struct aes_gcm_stream *s,
const struct aes_gcm_ctx *ctx, const uint8_t *nonce, size_t nonce_len)
{
s->g = ctx;
s->y_hi = 0;
s->y_lo = 0;
s->aad_len = 0;
s->ct_len = 0;
s->part_len = 0;
aes_gcm_j0(ctx, nonce, nonce_len, s->j0);
}
/* GHASH takes whole blocks; a piece's tail waits in part for the
* next piece of the same input, and is padded where that input ends. */
static void aes_gcm_stream_feed(struct aes_gcm_stream *s,
const uint8_t *data, size_t len)
{
size_t bulk;
if (s->part_len)
{
size_t take = 16 - s->part_len;
if (take > len)
take = len;
memcpy(s->part + s->part_len, data, take);
s->part_len += take;
data += take;
len -= take;
if (s->part_len < 16)
return;
aes_gcm_ghash_update(s->g, &s->y_hi, &s->y_lo, s->part, 16);
s->part_len = 0;
}
bulk = len & ~(size_t)15;
if (bulk)
aes_gcm_ghash_update(s->g, &s->y_hi, &s->y_lo, data, bulk);
memcpy(s->part, data + bulk, len - bulk);
s->part_len = len - bulk;
}
static void aes_gcm_stream_pad(struct aes_gcm_stream *s)
{
if (s->part_len)
{
aes_gcm_ghash_update(s->g, &s->y_hi, &s->y_lo, s->part, s->part_len);
s->part_len = 0;
}
}
void aes_gcm_stream_aad(struct aes_gcm_stream *s,
const uint8_t *aad, size_t len)
{
aes_gcm_stream_feed(s, aad, len);
s->aad_len += len;
}
void aes_gcm_stream_hash_ct(struct aes_gcm_stream *s,
const uint8_t *ct, size_t len)
{
if (!s->ct_len)
aes_gcm_stream_pad(s); /* the AAD ends here */
aes_gcm_stream_feed(s, ct, len);
s->ct_len += len;
}
void aes_gcm_stream_tag(struct aes_gcm_stream *s, uint8_t *tag)
{
unsigned i;
uint8_t ek[16];
aes_gcm_stream_pad(s);
s->y_hi ^= s->aad_len * 8;
s->y_lo ^= s->ct_len * 8;
aes_gcm_mul(s->g, &s->y_hi, &s->y_lo);
aes_encrypt_block(&s->g->aes, s->j0, ek);
crypto_store64_be(tag, s->y_hi);
crypto_store64_be(tag + 8, s->y_lo);
for (i = 0; i < 16; i++)
tag[i] ^= ek[i];
crypto_memzero(ek, sizeof(ek));
}
void aes_gcm_stream_crypt(const struct aes_gcm_stream *s, uint64_t off,
const uint8_t *in, uint8_t *out, size_t len)
{
uint8_t ctr[16];
size_t pos = (size_t)(off & 15);
/* the ciphertext's block b runs under counter J0 + 1 + b */
memcpy(ctr, s->j0, 16);
crypto_store32_be(ctr + 12, crypto_load32_be(s->j0 + 12)
+ 1 + (uint32_t)(off >> 4));
if (pos)
{
uint8_t ks[16];
size_t i, n = 16 - pos;
if (n > len)
n = len;
aes_encrypt_block(&s->g->aes, ctr, ks);
aes_ctr_inc32(ctr);
for (i = 0; i < n; i++)
out[i] = in[i] ^ ks[pos + i];
crypto_memzero(ks, sizeof(ks));
in += n;
out += n;
len -= n;
}
if (len)
aes_ctr_crypt(&s->g->aes, ctr, in, out, len);
crypto_memzero(ctr, sizeof(ctr));
}
#define QR(a, b, c, d) \
a += b; d ^= a; d = crypto_rotl32(d, 16); \
c += d; b ^= c; b = crypto_rotl32(b, 12); \
+483 -9
View File
@@ -37,7 +37,8 @@
*
* Three implementations, in descending order of speed:
*
* 1. Carry-less multiply folding (x86 PCLMULQDQ, chosen at runtime).
* 1. Carry-less multiply folding (x86 PCLMULQDQ, chosen at runtime),
* 512 bits wide where AVX-512 has VPCLMULQDQ.
* 2. ARMv8 CRC32 instructions (chosen at compile time).
* 3. Slicing-by-8 over a const table (every other target, both endians).
*
@@ -98,6 +99,34 @@
&& (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4))
/* GCC 4.4 introduced both wmmintrin.h and x86 target attributes. */
# define CRC32_HAVE_PCLMUL_PATH 1
# endif
/* VPCLMULQDQ on ZMM: GCC 8, clang 6 (Apple's 11), Visual Studio 2019;
* clang for PlayStation and clang-cl declare it only where the whole
* build has it. x86-64 only, as XXH3's AVX-512 is. */
# if defined(CRC32_HAVE_PCLMUL_PATH) && (defined(__x86_64__) || defined(_M_X64))
# if defined(__clang__) && (defined(__SCE__) || defined(_MSC_VER))
# if defined(__VPCLMULQDQ__) && defined(__AVX512BW__) \
&& defined(__AVX512VL__)
# define CRC32_HAVE_VPCLMUL_PATH 1
# define CRC32_TARGET_VPCLMUL
# endif
# elif defined(_MSC_VER)
# if _MSC_VER >= 1920
# define CRC32_HAVE_VPCLMUL_PATH 1
# define CRC32_TARGET_VPCLMUL
# endif
# elif defined(__clang__)
# if (defined(__apple_build_version__) && __clang_major__ >= 11) \
|| (!defined(__apple_build_version__) && __clang_major__ >= 6)
# define CRC32_HAVE_VPCLMUL_PATH 1
# endif
# elif defined(__GNUC__) && __GNUC__ >= 8
# define CRC32_HAVE_VPCLMUL_PATH 1
# endif
# if defined(CRC32_HAVE_VPCLMUL_PATH) && !defined(CRC32_TARGET_VPCLMUL)
# define CRC32_TARGET_VPCLMUL __attribute__((target( \
"avx512f,avx512bw,avx512vl,vpclmulqdq,pclmul")))
# endif
# endif
#endif
@@ -208,6 +237,9 @@ static INLINE __m128i crc32_fold16(__m128i x, __m128i k)
_mm_clmulepi64_si128(x, k, 0x11));
}
CRC32_TARGET_PCLMUL
static uint32_t crc32_pclmul_tail(__m128i x0, const uint8_t *data, size_t len);
CRC32_TARGET_PCLMUL
static uint32_t crc32_pclmul(uint32_t crc, const uint8_t *data, size_t len)
{
@@ -215,12 +247,7 @@ static uint32_t crc32_pclmul(uint32_t crc, const uint8_t *data, size_t len)
(int)CRC32_K1_HI, (int)CRC32_K1_LO);
const __m128i k3k4 = _mm_set_epi32((int)CRC32_K4_HI, (int)CRC32_K4_LO,
(int)CRC32_K3_HI, (int)CRC32_K3_LO);
const __m128i k5 = _mm_set_epi32(0, 0,
(int)CRC32_K5_HI, (int)CRC32_K5_LO);
const __m128i mupp = _mm_set_epi32((int)CRC32_PP_HI, (int)CRC32_PP_LO,
(int)CRC32_MU_HI, (int)CRC32_MU_LO);
const __m128i mask32 = _mm_set_epi32(0, 0, 0, -1);
__m128i x0, x1, x2, x3, t;
__m128i x0, x1, x2, x3;
/* Below four accumulators' worth, the reduction tail dominates. */
if (len < 64)
@@ -253,6 +280,23 @@ static uint32_t crc32_pclmul(uint32_t crc, const uint8_t *data, size_t len)
x1 = _mm_xor_si128(crc32_fold16(x0, k3k4), x1);
x2 = _mm_xor_si128(crc32_fold16(x1, k3k4), x2);
x0 = _mm_xor_si128(crc32_fold16(x2, k3k4), x3);
return crc32_pclmul_tail(x0, data, len);
}
/* One 128-bit accumulator and what is left, under 64 octets more than
* its 16: folded on 16 at a time, then reduced to the remainder */
CRC32_TARGET_PCLMUL
static uint32_t crc32_pclmul_tail(__m128i x0, const uint8_t *data, size_t len)
{
const __m128i k3k4 = _mm_set_epi32((int)CRC32_K4_HI, (int)CRC32_K4_LO,
(int)CRC32_K3_HI, (int)CRC32_K3_LO);
const __m128i k5 = _mm_set_epi32(0, 0,
(int)CRC32_K5_HI, (int)CRC32_K5_LO);
const __m128i mupp = _mm_set_epi32((int)CRC32_PP_HI, (int)CRC32_PP_LO,
(int)CRC32_MU_HI, (int)CRC32_MU_LO);
const __m128i mask32 = _mm_set_epi32(0, 0, 0, -1);
__m128i t;
uint32_t crc;
while (len >= 16)
{
@@ -282,6 +326,120 @@ static uint32_t crc32_pclmul(uint32_t crc, const uint8_t *data, size_t len)
return crc;
}
#if defined(CRC32_HAVE_VPCLMUL_PATH)
#include <immintrin.h>
#include <retro_atomic.h>
#if defined(_MSC_VER)
#include <intrin.h>
#else
#include <cpuid.h>
#endif
/* AVX-512 with VPCLMULQDQ: four 128-bit lanes to a register, four
* registers, 256 octets a turn; then one register 64 at a time, its
* lanes folded into the one the 128-bit tail goes on with */
/* The K() above of each lane: x^(4*512 + 32) and x^(4*512 - 32), and so
* on, as {high dword, low dword} pairs, the - 32 one first */
#define CRC32_K2048 0x00000001, 0x322D1430, 0x00000001, 0x1542778A
#define CRC32_K512 0x00000001, 0xC6E41596, 0x00000001, 0x54442BD4
#define CRC32_K384 0x00000001, 0x74359406, 0x00000000, 0x3DB1ECDC
#define CRC32_K256 0x00000001, 0x5A546366, 0x00000000, 0xF1DA05AA
#define CRC32_K128 0x00000000, 0xCCAA009E, 0x00000001, 0x751997D0
#define CRC32_LANE(k) _mm512_broadcast_i32x4(_mm_set_epi32(k))
#define CRC32_FOLD64(x, k, d) _mm512_ternarylogic_epi64( \
_mm512_clmulepi64_epi128((x), (k), 0x00), \
_mm512_clmulepi64_epi128((x), (k), 0x11), (d), 0x96)
CRC32_TARGET_VPCLMUL
static uint32_t crc32_vpclmul(uint32_t crc, const uint8_t *data, size_t len)
{
const __m512i k2048 = CRC32_LANE(CRC32_K2048);
const __m512i k512 = CRC32_LANE(CRC32_K512);
/* lane 0 on past three, 1 past two, 2 past one, 3 as it is */
const __m512i kl = _mm512_inserti32x4(_mm512_inserti32x4(
_mm512_inserti32x4(_mm512_setzero_si512(),
_mm_set_epi32(CRC32_K384), 0), _mm_set_epi32(CRC32_K256), 1),
_mm_set_epi32(CRC32_K128), 2);
__m512i z0, z1, z2, z3;
__m256i y;
z0 = _mm512_xor_si512(_mm512_loadu_si512((const void*)data),
_mm512_castsi128_si512(_mm_cvtsi32_si128((int)crc)));
z1 = _mm512_loadu_si512((const void*)(data + 64));
z2 = _mm512_loadu_si512((const void*)(data + 128));
z3 = _mm512_loadu_si512((const void*)(data + 192));
data += 256;
len -= 256;
while (len >= 256)
{
z0 = CRC32_FOLD64(z0, k2048, _mm512_loadu_si512((const void*)data));
z1 = CRC32_FOLD64(z1, k2048,
_mm512_loadu_si512((const void*)(data + 64)));
z2 = CRC32_FOLD64(z2, k2048,
_mm512_loadu_si512((const void*)(data + 128)));
z3 = CRC32_FOLD64(z3, k2048,
_mm512_loadu_si512((const void*)(data + 192)));
data += 256;
len -= 256;
}
z1 = CRC32_FOLD64(z0, k512, z1);
z2 = CRC32_FOLD64(z1, k512, z2);
z0 = CRC32_FOLD64(z2, k512, z3);
while (len >= 64)
{
z0 = CRC32_FOLD64(z0, k512, _mm512_loadu_si512((const void*)data));
data += 64;
len -= 64;
}
z0 = _mm512_mask_mov_epi64(CRC32_FOLD64(z0, kl, _mm512_setzero_si512()),
0xC0, z0);
y = _mm256_xor_si256(_mm512_castsi512_si256(z0),
_mm512_extracti64x4_epi64(z0, 1));
return crc32_pclmul_tail(_mm_xor_si128(_mm256_castsi256_si128(y),
_mm256_extracti128_si256(y, 1)), data, len);
}
#undef CRC32_FOLD64
#undef CRC32_LANE
/* AVX-512, which says the OS keeps ZMM state, and leaf 7's VPCLMULQDQ
* (ECX bit 10): asked once */
static retro_atomic_int_t crc32_vp_state; /* 0 not yet, 1 no, 2 yes */
static int crc32_have_vpclmul(void)
{
int st = retro_atomic_load_acquire_int(&crc32_vp_state);
if (!st)
{
uint32_t ecx = 0;
if (cpu_features_get() & RETRO_SIMD_AVX512)
{
#if defined(_MSC_VER)
int r[4];
__cpuidex(r, 0, 0);
if (r[0] >= 7)
{
__cpuidex(r, 7, 0);
ecx = (uint32_t)r[2];
}
#else
unsigned a, b, c, d;
if (__get_cpuid_max(0, NULL) >= 7)
{
__cpuid_count(7, 0, a, b, c, d);
ecx = c;
}
#endif
}
st = (ecx & (1u << 10)) ? 2 : 1;
retro_atomic_store_release_int(&crc32_vp_state, st);
}
return st == 2;
}
#endif
/* Detection lives in cpu_features_get(), which probes once behind an
* acquire/release pair and is the frontend's single answer to "what
* does this CPU have". PCLMULQDQ needs no OSXSAVE/XCR0 dance: it is
@@ -292,6 +450,211 @@ static int crc32_have_pclmul(void)
return (cpu_features_get() & RETRO_SIMD_PCLMUL) != 0;
}
/* clang for PlayStation and clang-cl: where the build has the ISAs */
#if !(defined(__clang__) && (defined(_MSC_VER) || defined(__SCE__))) \
|| (defined(__SSSE3__) && defined(__PCLMUL__))
/* ------------------------------------------------------------------ */
/* MSB-first CRCs by carry-less folding: Ogg's CRC-32, CRC-16/CCITT */
/* ------------------------------------------------------------------ */
/* Neither is reflected, so the octets are the polynomial as they come:
* each 16 of them, byte-swapped, is 128 coefficients with the first
* octet's top bit the highest. The register's seed is the same as that
* many bits added to the front of the message. Folding keeps one
* accumulator A with A ~ every octet so far (mod P), placed as their
* last 16: A * x^D = A_hi * x^(D+64) + A_lo * x^D, each part times
* x^n mod P, which for a 16- or 32-bit P leaves the product inside 128
* bits. A itself and what is left after it are then the table path's,
* from a seed of 0, which needs no reduction of its own.
*
* The constants are x^n mod P, for 0x104C11DB7 and 0x11021 */
static uint32_t crc32_ogg_table(uint32_t crc, const uint8_t *data,
size_t len);
static uint32_t crc16_ccitt_table(uint32_t crc, const uint8_t *data,
size_t len);
typedef uint32_t (*crc_msb_table_t)(uint32_t crc, const uint8_t *data,
size_t len);
typedef struct crc_msb
{
uint32_t k576, k512; /* four accumulators on */
uint32_t k192, k128; /* one */
uint32_t k2112, k2048; /* four 512-bit ones on */
uint32_t k448, k384, k320, k256; /* lanes into one */
unsigned width;
crc_msb_table_t table;
} crc_msb_t;
static const crc_msb_t crc_msb_ogg = {
0x8833794CU, 0xE6228B11U, 0xC5B9CD4CU, 0xE8A45605U,
0xCBCF3BCBU, 0x88FE2237U,
0x64BF7A9BU, 0x8C3828A8U, 0x569700E5U, 0x75BE46B7U,
32, crc32_ogg_table
};
static const crc_msb_t crc_msb_c16 = {
0x8832U, 0x13FCU, 0x650BU, 0xAEFCU,
0xF17EU, 0xFD50U,
0x2535U, 0xCDE2U, 0x26AAU, 0x8E29U,
16, crc16_ccitt_table
};
#if defined(_MSC_VER)
#include <tmmintrin.h>
#define CRC32_TARGET_MSB
#else
#include <tmmintrin.h>
#define CRC32_TARGET_MSB __attribute__((target("pclmul,ssse3")))
#endif
#define CRC_MSB_K(hi, lo) _mm_set_epi32(0, (int)(hi), 0, (int)(lo))
#define CRC_MSB_REV _mm_setr_epi8(15, 14, 13, 12, 11, 10, 9, 8, \
7, 6, 5, 4, 3, 2, 1, 0)
#define CRC_MSB_LOAD(p) _mm_shuffle_epi8( \
_mm_loadu_si128((const __m128i*)(const void*)(p)), rev)
CRC32_TARGET_MSB
static INLINE __m128i crc_msb_fold(__m128i x, __m128i k, __m128i d)
{
return _mm_xor_si128(_mm_xor_si128(_mm_clmulepi64_si128(x, k, 0x11),
_mm_clmulepi64_si128(x, k, 0x00)), d);
}
/* One accumulator, the octets after it 16 at a time, then it and the
* rest through the table */
CRC32_TARGET_MSB
static uint32_t crc_msb_tail(const crc_msb_t *m, __m128i x0,
const uint8_t *data, size_t len)
{
const __m128i rev = CRC_MSB_REV;
const __m128i k128 = CRC_MSB_K(m->k192, m->k128);
uint8_t a[16];
while (len >= 16)
{
x0 = crc_msb_fold(x0, k128, CRC_MSB_LOAD(data));
data += 16;
len -= 16;
}
_mm_storeu_si128((__m128i*)(void*)a, _mm_shuffle_epi8(x0, rev));
return m->table(m->table(0, a, 16), data, len);
}
/* At least 64 octets, from the register @crc */
CRC32_TARGET_MSB
static uint32_t crc_msb_pclmul(const crc_msb_t *m, uint32_t crc,
const uint8_t *data, size_t len)
{
const __m128i rev = CRC_MSB_REV;
const __m128i k512 = CRC_MSB_K(m->k576, m->k512);
const __m128i k128 = CRC_MSB_K(m->k192, m->k128);
__m128i x0, x1, x2, x3;
x0 = _mm_xor_si128(CRC_MSB_LOAD(data),
_mm_set_epi32((int)(crc << (32 - m->width)), 0, 0, 0));
x1 = CRC_MSB_LOAD(data + 16);
x2 = CRC_MSB_LOAD(data + 32);
x3 = CRC_MSB_LOAD(data + 48);
data += 64;
len -= 64;
while (len >= 64)
{
x0 = crc_msb_fold(x0, k512, CRC_MSB_LOAD(data));
x1 = crc_msb_fold(x1, k512, CRC_MSB_LOAD(data + 16));
x2 = crc_msb_fold(x2, k512, CRC_MSB_LOAD(data + 32));
x3 = crc_msb_fold(x3, k512, CRC_MSB_LOAD(data + 48));
data += 64;
len -= 64;
}
x1 = crc_msb_fold(x0, k128, x1);
x2 = crc_msb_fold(x1, k128, x2);
x0 = crc_msb_fold(x2, k128, x3);
return crc_msb_tail(m, x0, data, len);
}
#if defined(CRC32_HAVE_VPCLMUL_PATH)
#define CRC_MSB_K4(hi, lo) _mm512_broadcast_i32x4(CRC_MSB_K(hi, lo))
#define CRC_MSB_FOLD64(x, k, d) _mm512_ternarylogic_epi64( \
_mm512_clmulepi64_epi128((x), (k), 0x00), \
_mm512_clmulepi64_epi128((x), (k), 0x11), (d), 0x96)
#define CRC_MSB_LOAD64(p) _mm512_shuffle_epi8( \
_mm512_loadu_si512((const void*)(p)), rev4)
/* At least 256 octets: four 512-bit accumulators, then one, its lanes
* into the 128-bit tail */
CRC32_TARGET_VPCLMUL
static uint32_t crc_msb_vpclmul(const crc_msb_t *m, uint32_t crc,
const uint8_t *data, size_t len)
{
const __m512i rev4 = _mm512_broadcast_i32x4(CRC_MSB_REV);
const __m512i k2048 = CRC_MSB_K4(m->k2112, m->k2048);
const __m512i k512 = CRC_MSB_K4(m->k576, m->k512);
const __m512i kl = _mm512_inserti32x4(_mm512_inserti32x4(
_mm512_inserti32x4(_mm512_setzero_si512(),
CRC_MSB_K(m->k448, m->k384), 0), CRC_MSB_K(m->k320, m->k256), 1),
CRC_MSB_K(m->k192, m->k128), 2);
__m512i z0, z1, z2, z3;
__m256i y;
z0 = _mm512_xor_si512(CRC_MSB_LOAD64(data), _mm512_castsi128_si512(
_mm_set_epi32((int)(crc << (32 - m->width)), 0, 0, 0)));
z1 = CRC_MSB_LOAD64(data + 64);
z2 = CRC_MSB_LOAD64(data + 128);
z3 = CRC_MSB_LOAD64(data + 192);
data += 256;
len -= 256;
while (len >= 256)
{
z0 = CRC_MSB_FOLD64(z0, k2048, CRC_MSB_LOAD64(data));
z1 = CRC_MSB_FOLD64(z1, k2048, CRC_MSB_LOAD64(data + 64));
z2 = CRC_MSB_FOLD64(z2, k2048, CRC_MSB_LOAD64(data + 128));
z3 = CRC_MSB_FOLD64(z3, k2048, CRC_MSB_LOAD64(data + 192));
data += 256;
len -= 256;
}
z1 = CRC_MSB_FOLD64(z0, k512, z1);
z2 = CRC_MSB_FOLD64(z1, k512, z2);
z0 = CRC_MSB_FOLD64(z2, k512, z3);
while (len >= 64)
{
z0 = CRC_MSB_FOLD64(z0, k512, CRC_MSB_LOAD64(data));
data += 64;
len -= 64;
}
z0 = _mm512_mask_mov_epi64(CRC_MSB_FOLD64(z0, kl, _mm512_setzero_si512()),
0xC0, z0);
y = _mm256_xor_si256(_mm512_castsi512_si256(z0),
_mm512_extracti64x4_epi64(z0, 1));
return crc_msb_tail(m, _mm_xor_si128(_mm256_castsi256_si128(y),
_mm256_extracti128_si256(y, 1)), data, len);
}
#undef CRC_MSB_LOAD64
#undef CRC_MSB_FOLD64
#undef CRC_MSB_K4
#endif
/* The fold, where the CPU has it and @len is worth it; else the table */
static uint32_t crc_msb(const crc_msb_t *m, uint32_t crc,
const uint8_t *data, size_t len)
{
#if defined(CRC32_HAVE_VPCLMUL_PATH)
if (len >= 256 && crc32_have_vpclmul())
return crc_msb_vpclmul(m, crc, data, len);
#endif
if ( len >= 64
&& (cpu_features_get() & (RETRO_SIMD_PCLMUL | RETRO_SIMD_SSSE3))
== (RETRO_SIMD_PCLMUL | RETRO_SIMD_SSSE3))
return crc_msb_pclmul(m, crc, data, len);
return m->table(crc, data, len);
}
#undef CRC_MSB_LOAD
#undef CRC_MSB_REV
#undef CRC_MSB_K
#define CRC32_HAVE_MSB_PATH 1
#endif
#endif /* x86 */
/* ------------------------------------------------------------------ */
@@ -321,6 +684,73 @@ static int crc32_have_pclmul(void)
#define CRC32_TARGET_ARM
#endif
/* Three chains of CRC32X, each a lane of 1 KiB, keep the instruction's
* latency of two or three cycles filled where one chain waits on it.
* The lanes are put back together as one moved on past a lane's zeros
* is a linear map of the register, a nibble at a time:
* crc = on(on(c0) ^ c1) ^ c2
* crc32_arm_on[j][v] is v << 4j moved on 1024 zero octets. */
#define CRC32_ARM_LANE 1024
static const uint32_t crc32_arm_on[8][16] = {
{
0x00000000U, 0xF891F16FU, 0x2A52E49FU, 0xD2C315F0U, 0x54A5C93EU,
0xAC343851U, 0x7EF72DA1U, 0x8666DCCEU, 0xA94B927CU, 0x51DA6313U,
0x831976E3U, 0x7B88878CU, 0xFDEE5B42U, 0x057FAA2DU, 0xD7BCBFDDU,
0x2F2D4EB2U
},
{
0x00000000U, 0x89E622B9U, 0xC8BD4333U, 0x415B618AU, 0x4A0B8027U,
0xC3EDA29EU, 0x82B6C314U, 0x0B50E1ADU, 0x9417004EU, 0x1DF122F7U,
0x5CAA437DU, 0xD54C61C4U, 0xDE1C8069U, 0x57FAA2D0U, 0x16A1C35AU,
0x9F47E1E3U
},
{
0x00000000U, 0xF35F06DDU, 0x3DCF0BFBU, 0xCE900D26U, 0x7B9E17F6U,
0x88C1112BU, 0x46511C0DU, 0xB50E1AD0U, 0xF73C2FECU, 0x04632931U,
0xCAF32417U, 0x39AC22CAU, 0x8CA2381AU, 0x7FFD3EC7U, 0xB16D33E1U,
0x4232353CU
},
{
0x00000000U, 0x35095999U, 0x6A12B332U, 0x5F1BEAABU, 0xD4256664U,
0xE12C3FFDU, 0xBE37D556U, 0x8B3E8CCFU, 0x733BCA89U, 0x46329310U,
0x192979BBU, 0x2C202022U, 0xA71EACEDU, 0x9217F574U, 0xCD0C1FDFU,
0xF8054646U
},
{
0x00000000U, 0xE6779512U, 0x179E2C65U, 0xF1E9B977U, 0x2F3C58CAU,
0xC94BCDD8U, 0x38A274AFU, 0xDED5E1BDU, 0x5E78B194U, 0xB80F2486U,
0x49E69DF1U, 0xAF9108E3U, 0x7144E95EU, 0x97337C4CU, 0x66DAC53BU,
0x80AD5029U
},
{
0x00000000U, 0xBCF16328U, 0xA293C011U, 0x1E62A339U, 0x9E568663U,
0x22A7E54BU, 0x3CC54672U, 0x8034255AU, 0xE7DC0A87U, 0x5B2D69AFU,
0x454FCA96U, 0xF9BEA9BEU, 0x798A8CE4U, 0xC57BEFCCU, 0xDB194CF5U,
0x67E82FDDU
},
{
0x00000000U, 0x14C9134FU, 0x2992269EU, 0x3D5B35D1U, 0x53244D3CU,
0x47ED5E73U, 0x7AB66BA2U, 0x6E7F78EDU, 0xA6489A78U, 0xB2818937U,
0x8FDABCE6U, 0x9B13AFA9U, 0xF56CD744U, 0xE1A5C40BU, 0xDCFEF1DAU,
0xC837E295U
},
{
0x00000000U, 0x97E032B1U, 0xF4B16323U, 0x63515192U, 0x3213C007U,
0xA5F3F2B6U, 0xC6A2A324U, 0x51429195U, 0x6427800EU, 0xF3C7B2BFU,
0x9096E32DU, 0x0776D19CU, 0x56344009U, 0xC1D472B8U, 0xA285232AU,
0x3565119BU
}
};
static INLINE uint32_t crc32_arm_lane_on(uint32_t c)
{
return crc32_arm_on[0][ c & 15] ^ crc32_arm_on[1][(c >> 4) & 15]
^ crc32_arm_on[2][(c >> 8) & 15] ^ crc32_arm_on[3][(c >> 12) & 15]
^ crc32_arm_on[4][(c >> 16) & 15] ^ crc32_arm_on[5][(c >> 20) & 15]
^ crc32_arm_on[6][(c >> 24) & 15] ^ crc32_arm_on[7][ c >> 28 ];
}
CRC32_TARGET_ARM
static uint32_t crc32_arm(uint32_t crc, const uint8_t *data, size_t len)
{
@@ -330,6 +760,24 @@ static uint32_t crc32_arm(uint32_t crc, const uint8_t *data, size_t len)
data++;
len--;
}
while (len >= 3 * CRC32_ARM_LANE)
{
uint32_t c1 = 0, c2 = 0;
size_t i;
for (i = 0; i < CRC32_ARM_LANE; i += 8)
{
uint64_t v0, v1, v2;
memcpy(&v0, data + i, sizeof(v0));
memcpy(&v1, data + CRC32_ARM_LANE + i, sizeof(v1));
memcpy(&v2, data + 2 * CRC32_ARM_LANE + i, sizeof(v2));
crc = __crc32d(crc, v0);
c1 = __crc32d(c1, v1);
c2 = __crc32d(c2, v2);
}
crc = crc32_arm_lane_on(crc32_arm_lane_on(crc) ^ c1) ^ c2;
data += 3 * CRC32_ARM_LANE;
len -= 3 * CRC32_ARM_LANE;
}
while (len >= 8)
{
uint64_t v;
@@ -382,8 +830,10 @@ static uint32_t crc32_arm(uint32_t crc, const uint8_t *data, size_t len)
* than of any particular CRC.
*/
uint16_t encoding_crc16_ccitt(uint16_t crc, const uint8_t *data, size_t len)
static uint32_t crc16_ccitt_table(uint32_t seed, const uint8_t *data,
size_t len)
{
uint16_t crc = (uint16_t)seed;
while (len >= 8)
{
crc = (uint16_t)(
@@ -412,7 +862,8 @@ uint16_t encoding_crc16_ccitt(uint16_t crc, const uint8_t *data, size_t len)
return crc;
}
uint32_t encoding_crc32_ogg(uint32_t crc, const uint8_t *data, size_t len)
static uint32_t crc32_ogg_table(uint32_t crc, const uint8_t *data,
size_t len)
{
/* Slicing-by-8, as for the reflected variant above, but assembling
* each word from bytes rather than loading it. The reflected path
@@ -450,6 +901,24 @@ uint32_t encoding_crc32_ogg(uint32_t crc, const uint8_t *data, size_t len)
return crc;
}
uint16_t encoding_crc16_ccitt(uint16_t crc, const uint8_t *data, size_t len)
{
#if defined(CRC32_HAVE_MSB_PATH)
return (uint16_t)crc_msb(&crc_msb_c16, crc, data, len);
#else
return (uint16_t)crc16_ccitt_table(crc, data, len);
#endif
}
uint32_t encoding_crc32_ogg(uint32_t crc, const uint8_t *data, size_t len)
{
#if defined(CRC32_HAVE_MSB_PATH)
return crc_msb(&crc_msb_ogg, crc, data, len);
#else
return crc32_ogg_table(crc, data, len);
#endif
}
uint32_t encoding_crc32(uint32_t crc, const uint8_t *data, size_t len)
{
crc = ~crc;
@@ -462,6 +931,11 @@ uint32_t encoding_crc32(uint32_t crc, const uint8_t *data, size_t len)
#elif defined(CRC32_HAVE_ARM_PATH)
crc = crc32_arm(crc, data, len);
#elif defined(CRC32_HAVE_PCLMUL_PATH)
#if defined(CRC32_HAVE_VPCLMUL_PATH)
if (len >= 256 && crc32_have_vpclmul())
crc = crc32_vpclmul(crc, data, len);
else
#endif
if (len >= 64 && crc32_have_pclmul())
crc = crc32_pclmul(crc, data, len);
else
+728 -5
View File
@@ -139,6 +139,124 @@ static void rd_adler_blocks(uint32_t *pa, uint32_t *pb,
*pa = a + rd_adler_hsum(vs1);
}
/* Wider blocks, chosen at runtime: AVX2's 32 octets and AVX-512's 64, the
* weighted sum by pmaddubsw - each pair of octets times weights of 64 at
* most is under 32767 - and pmaddwd. The sums that make b only part it
* up differently, so NMAX holds as it is. AVX-512 on x86-64 only, as the
* other AVX-512 paths are; clang for PlayStation and clang-cl declare
* the intrinsics only where the whole build has them. */
# if defined(__clang__) && (defined(__SCE__) || defined(_MSC_VER))
# if defined(__AVX2__)
# define RD_ADLER_AVX2 1
# endif
# if defined(__AVX512BW__) && (defined(__x86_64__) || defined(_M_X64))
# define RD_ADLER_AVX512 1
# endif
# define RD_TARGET(t)
# elif defined(_MSC_VER)
# if _MSC_VER >= 1700
# define RD_ADLER_AVX2 1
# endif
# if _MSC_VER >= 1911 && defined(_M_X64)
# define RD_ADLER_AVX512 1
# endif
# define RD_TARGET(t)
# elif (defined(__clang__) && __clang_major__ >= 4) \
|| (!defined(__clang__) && defined(__GNUC__) && __GNUC__ >= 5)
# define RD_ADLER_AVX2 1
# if defined(__x86_64__)
# define RD_ADLER_AVX512 1
# endif
# define RD_TARGET(t) __attribute__((target(t)))
# endif
# if defined(RD_ADLER_AVX2)
# include <immintrin.h>
# include <features/features_cpu.h>
RD_TARGET("avx2")
static void rd_adler_blocks32(uint32_t *pa, uint32_t *pb,
const uint8_t *buf, size_t blocks)
{
const __m256i zero = _mm256_setzero_si256();
const __m256i w = _mm256_setr_epi8(32, 31, 30, 29, 28, 27, 26, 25,
24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9,
8, 7, 6, 5, 4, 3, 2, 1);
const __m256i ones = _mm256_set1_epi16(1);
__m256i vs1 = zero;
__m256i vps = zero;
__m256i vs2 = zero;
uint32_t a = *pa;
uint32_t b = *pb;
size_t n = blocks;
while (n--)
{
__m256i v = _mm256_loadu_si256((const __m256i*)buf);
vps = _mm256_add_epi32(vps, vs1);
vs1 = _mm256_add_epi32(vs1, _mm256_sad_epu8(v, zero));
vs2 = _mm256_add_epi32(vs2,
_mm256_madd_epi16(_mm256_maddubs_epi16(v, w), ones));
buf += 32;
}
*pb = b + (uint32_t)(blocks << 5) * a
+ (rd_adler_hsum(_mm_add_epi32(_mm256_castsi256_si128(vps),
_mm256_extracti128_si256(vps, 1))) << 5)
+ rd_adler_hsum(_mm_add_epi32(_mm256_castsi256_si128(vs2),
_mm256_extracti128_si256(vs2, 1)));
*pa = a + rd_adler_hsum(_mm_add_epi32(_mm256_castsi256_si128(vs1),
_mm256_extracti128_si256(vs1, 1)));
}
# if defined(RD_ADLER_AVX512)
RD_TARGET("avx512f,avx512bw")
static void rd_adler_blocks64(uint32_t *pa, uint32_t *pb,
const uint8_t *buf, size_t blocks)
{
const __m512i zero = _mm512_setzero_si512();
const __m512i w = _mm512_set_epi8(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43,
44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
60, 61, 62, 63, 64);
const __m512i ones = _mm512_set1_epi16(1);
__m512i vs1 = zero;
__m512i vps = zero;
__m512i vs2 = zero;
uint32_t a = *pa;
uint32_t b = *pb;
size_t n = blocks;
while (n--)
{
__m512i v = _mm512_loadu_si512((const void*)buf);
vps = _mm512_add_epi32(vps, vs1);
vs1 = _mm512_add_epi32(vs1, _mm512_sad_epu8(v, zero));
vs2 = _mm512_add_epi32(vs2,
_mm512_madd_epi16(_mm512_maddubs_epi16(v, w), ones));
buf += 64;
}
*pb = b + (uint32_t)(blocks << 6) * a
+ ((uint32_t)_mm512_reduce_add_epi32(vps) << 6)
+ (uint32_t)_mm512_reduce_add_epi32(vs2);
*pa = a + (uint32_t)_mm512_reduce_add_epi32(vs1);
}
# endif
/* The widest blocks this CPU and its OS take: 64, 32 or 16 */
static unsigned rd_adler_widest(void)
{
uint64_t simd = cpu_features_get();
# if defined(RD_ADLER_AVX512)
if (simd & RETRO_SIMD_AVX512)
return 64;
# endif
return (simd & RETRO_SIMD_AVX2) ? 32 : 16;
}
# endif
#elif defined(__ARM_NEON) || defined(__ARM_NEON__) || defined(HAVE_NEON)
#include <arm_neon.h>
#define RD_ADLER_SIMD 1
@@ -221,12 +339,29 @@ static void rd_adler_blocks(uint32_t *pa, uint32_t *pb,
#define RD_ADLER_BLOCK 16
#endif
/* Fold up to RD_ADLER_NMAX bytes into (a, b) with no reduction inside. */
/* Fold up to RD_ADLER_NMAX bytes into (a, b) with no reduction inside:
* the widest blocks first, then 16 at a time, then a byte at a time. */
static void rd_adler_run(uint32_t *pa, uint32_t *pb,
const uint8_t *buf, size_t n)
const uint8_t *buf, size_t n, unsigned wide)
{
uint32_t a = *pa;
uint32_t b = *pb;
#if defined(RD_ADLER_AVX2)
if (wide > 16 && n >= wide)
{
size_t blocks = n / wide;
# if defined(RD_ADLER_AVX512)
if (wide == 64)
rd_adler_blocks64(&a, &b, buf, blocks);
else
# endif
rd_adler_blocks32(&a, &b, buf, blocks);
buf += blocks * wide;
n -= blocks * wide;
}
#else
(void)wide;
#endif
if (n >= RD_ADLER_BLOCK)
{
size_t blocks = n / RD_ADLER_BLOCK;
@@ -244,8 +379,8 @@ static void rd_adler_run(uint32_t *pa, uint32_t *pb,
*pb = b;
}
static uint32_t rd_adler32_update(uint32_t adler,
const uint8_t *buf, size_t len)
static uint32_t rd_adler32_on(uint32_t adler, const uint8_t *buf,
size_t len, unsigned wide)
{
uint32_t a = adler & 0xffff;
uint32_t b = (adler >> 16) & 0xffff;
@@ -253,7 +388,7 @@ static uint32_t rd_adler32_update(uint32_t adler,
{
size_t n = len > RD_ADLER_NMAX ? RD_ADLER_NMAX : len;
len -= n;
rd_adler_run(&a, &b, buf, n);
rd_adler_run(&a, &b, buf, n, wide);
buf += n;
a %= RD_ADLER_MODULUS;
b %= RD_ADLER_MODULUS;
@@ -261,6 +396,17 @@ static uint32_t rd_adler32_update(uint32_t adler,
return (b << 16) | a;
}
static uint32_t rd_adler32_update(uint32_t adler,
const uint8_t *buf, size_t len)
{
#if defined(RD_ADLER_AVX2)
/* below two of the wide blocks the 16-octet ones do as well */
if (len >= 128)
return rd_adler32_on(adler, buf, len, rd_adler_widest());
#endif
return rd_adler32_on(adler, buf, len, 16);
}
/* Test entry point for tools/encodings/adler32_test.c: the kernel above is
* static, and driving it only through rinflate/rdeflate would exercise
* whatever lengths and alignments a stream happens to produce rather than
@@ -271,6 +417,23 @@ uint32_t rd_probe_adler32(uint32_t adler, const uint8_t *buf, size_t len)
return rd_adler32_update(adler, buf, len);
}
/* The widest blocks this CPU takes, and the same as rd_probe_adler32()
* through blocks of @wide octets, 16, 32 or 64, no wider than that */
unsigned rd_probe_adler32_widest(void)
{
#if defined(RD_ADLER_AVX2)
return rd_adler_widest();
#else
return 16;
#endif
}
uint32_t rd_probe_adler32_on(uint32_t adler, const uint8_t *buf, size_t len,
unsigned wide)
{
return rd_adler32_on(adler, buf, len, wide);
}
/* ===================== inflate (RFC 1951 / RFC 1950) ===================== */
/* Clean-room RFC 1951 (DEFLATE) / RFC 1950 (zlib) inflate.
* Non-blocking, resumable: suspends when input is exhausted or output is
@@ -900,6 +1063,13 @@ void rinflate_set_out(void *data, uint8_t *out, size_t size)
s->wprior = s->whave;
}
void rinflate_extend_out(void *data, size_t size)
{
struct rinflate *s = (struct rinflate*)data;
if (size >= s->out_pos)
s->out_size = size;
}
/* Prime the back-reference window with the tail of @dict, for resuming
* raw-deflate decode mid-stream (indexed random access into gzip
* members: the index stores the 32KB of plaintext preceding each entry
@@ -3458,3 +3628,556 @@ suspend:
return RDEFLATE_PROCESS_END;
return RDEFLATE_PROCESS_NEXT;
}
/* -------- Deflate64, one-shot --------
*
* Deflate64 (PKWARE's "enhanced deflate", zip method 9, 7z's
* Deflate64) is RFC 1951 with a 64 KiB window, length code 285 taking
* 16 extra bits over a base of 3 rather than standing for 258, and
* distance codes 30 and 31 in use: 32769 and 49153, with 14 extra
* bits. A stream decodes into a buffer of the caller's a piece at a
* time, matches reaching back past it into a 64 KiB ring of what came
* before; the decoder above stays RFC 1951's alone. */
#define RINF64_LUT_BITS 9
struct rinf64_table
{
uint16_t lut[1 << RINF64_LUT_BITS]; /* symbol << 4 | length; 0: longer */
uint16_t count[16];
uint16_t sym[288];
};
#define RINF64_WINDOW 65536
enum rinf64_state
{
RINF64_HEADER = 0, /* a block's header next */
RINF64_SYMBOLS, /* in a coded block */
RINF64_STORED, /* in a stored block */
RINF64_DONE
};
struct rinf64
{
struct rinf64_table lit;
struct rinf64_table dist;
const uint8_t *p;
const uint8_t *end;
uint64_t bits;
uint64_t total; /* octets decoded so far */
size_t over; /* zeros read past the end */
unsigned n;
uint32_t copy_len; /* a match part way, */
uint32_t copy_dist; /* from this far back */
uint32_t stored_left;
uint32_t rpos; /* where the ring is written next */
int state;
int last; /* the block in hand is the last */
int held; /* a literal the out had no room
for, or -1 */
uint8_t ring[RINF64_WINDOW]; /* what came before the out */
};
static const uint16_t rinf64_len_base[29] = {
3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,
67,83,99,115,131,163,195,227,3 };
static const uint8_t rinf64_len_extra[29] = {
0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,16 };
static const uint16_t rinf64_dist_base[32] = {
1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,
1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,32769,49153 };
static const uint8_t rinf64_dist_extra[32] = {
0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,
14,14 };
/* At least @k bits in hand, k <= 57. With eight octets or more to go,
* the next eight are taken in one load and the whole ones that fit
* counted: the bits above n are then the octets still to come, which
* the next load puts there again, so they stay as they are. */
static INLINE void rinf64_need(struct rinf64 *s, unsigned k)
{
if (s->n >= k)
return;
if (s->end - s->p >= 8)
{
const uint8_t *q = s->p;
uint64_t w = (uint64_t)q[0] | ((uint64_t)q[1] << 8)
| ((uint64_t)q[2] << 16) | ((uint64_t)q[3] << 24)
| ((uint64_t)q[4] << 32) | ((uint64_t)q[5] << 40)
| ((uint64_t)q[6] << 48) | ((uint64_t)q[7] << 56);
s->bits |= w << s->n;
s->p += (63 - s->n) >> 3;
s->n |= 56;
return;
}
while (s->n < k)
{
uint64_t b = s->p < s->end ? *s->p++ : (s->over++, 0u);
s->bits |= b << s->n;
s->n += 8;
}
}
/* @k bits, 0 <= k <= 24 */
static INLINE uint32_t rinf64_bits(struct rinf64 *s, unsigned k)
{
uint32_t v;
rinf64_need(s, k);
v = (uint32_t)(s->bits & ((1u << k) - 1));
s->bits >>= k;
s->n -= k;
return v;
}
/* Canonical codes for @n lengths; incomplete codes are allowed, their
* unused codes failing to decode, oversubscribed ones are not. */
static int rinf64_build(struct rinf64_table *t, const uint8_t *len,
unsigned n)
{
uint16_t offs[16];
unsigned i, l;
int left = 1;
uint32_t code = 0;
memset(t->count, 0, sizeof(t->count));
for (i = 0; i < n; i++)
t->count[len[i]]++;
t->count[0] = 0;
for (l = 1; l < 16; l++)
{
left = (left << 1) - t->count[l];
if (left < 0)
return -1;
}
offs[1] = 0;
for (l = 1; l < 15; l++)
offs[l + 1] = (uint16_t)(offs[l] + t->count[l]);
for (i = 0; i < n; i++)
if (len[i])
t->sym[offs[len[i]]++] = (uint16_t)i;
/* each code no longer than the table looks, reversed, as it arrives */
memset(t->lut, 0, sizeof(t->lut));
for (l = 1, i = 0; l <= RINF64_LUT_BITS; l++)
{
unsigned c;
for (c = 0; c < t->count[l]; c++, code++, i++)
{
unsigned rev = 0, b, k;
for (b = 0; b < l; b++)
rev |= ((code >> b) & 1u) << (l - 1 - b);
for (k = rev; k < (1u << RINF64_LUT_BITS); k += 1u << l)
t->lut[k] = (uint16_t)((t->sym[i] << 4) | l);
}
code <<= 1;
}
return 0;
}
static INLINE int rinf64_decode_sym(struct rinf64 *s,
const struct rinf64_table *t)
{
uint32_t e;
rinf64_need(s, 15);
if ((e = t->lut[s->bits & ((1u << RINF64_LUT_BITS) - 1)]))
{
s->bits >>= e & 15;
s->n -= e & 15;
return (int)(e >> 4);
}
{
/* longer than the table: a bit at a time */
uint32_t code = 0, first = 0, index = 0;
unsigned l;
for (l = 1; l < 16; l++)
{
uint32_t cnt = t->count[l];
code |= (uint32_t)(s->bits & 1);
s->bits >>= 1;
s->n--;
if (code - first < cnt)
return t->sym[index + code - first];
index += cnt;
first = (first + cnt) << 1;
code <<= 1;
}
}
return -1;
}
/* The fixed codes, or a dynamic block's, into s->lit and s->dist */
static int rinf64_tables(struct rinf64 *s, int dynamic)
{
static const uint8_t order[19] =
{ 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 };
uint8_t len[288 + 32];
unsigned i, nlit = 288, ndist = 32;
if (!dynamic)
{
for (i = 0; i < 144; i++) len[i] = 8;
for (; i < 256; i++) len[i] = 9;
for (; i < 280; i++) len[i] = 7;
for (; i < 288; i++) len[i] = 8;
for (i = 0; i < 32; i++) len[288 + i] = 5;
if ( rinf64_build(&s->lit, len, 288)
|| rinf64_build(&s->dist, len + 288, 32))
return -1;
return 0;
}
{
uint8_t cl[19];
unsigned ncl, n = 0;
nlit = rinf64_bits(s, 5) + 257;
ndist = rinf64_bits(s, 5) + 1;
ncl = rinf64_bits(s, 4) + 4;
if (nlit > 286)
return -1;
memset(cl, 0, sizeof(cl));
for (i = 0; i < ncl; i++)
cl[order[i]] = (uint8_t)rinf64_bits(s, 3);
if (rinf64_build(&s->lit, cl, 19))
return -1;
while (n < nlit + ndist)
{
int sym = rinf64_decode_sym(s, &s->lit);
unsigned rep, val = 0;
if (sym < 0)
return -1;
if (sym < 16)
{
len[n++] = (uint8_t)sym;
continue;
}
if (sym == 16)
{
if (!n)
return -1;
val = len[n - 1];
rep = 3 + rinf64_bits(s, 2);
}
else if (sym == 17)
rep = 3 + rinf64_bits(s, 3);
else
rep = 11 + rinf64_bits(s, 7);
if (n + rep > nlit + ndist)
return -1;
while (rep--)
len[n++] = (uint8_t)val;
}
if (!len[256])
return -1; /* no end of block */
if ( rinf64_build(&s->lit, len, nlit)
|| rinf64_build(&s->dist, len + nlit, ndist))
return -1;
}
return 0;
}
void *rinflate64_new(void)
{
struct rinf64 *s = (struct rinf64*)malloc(sizeof(*s));
if (!s)
return NULL;
s->p = NULL;
s->end = NULL;
s->bits = 0;
s->total = 0;
s->over = 0;
s->n = 0;
s->copy_len = 0;
s->copy_dist = 0;
s->stored_left = 0;
s->rpos = 0;
s->state = RINF64_HEADER;
s->last = 0;
s->held = -1;
return s;
}
void rinflate64_free(void *stream)
{
free(stream);
}
/* @len octets of a match from @dist back into @o at @a: from the out
* where it reaches that far, from the ring before it. Where the out
* is then. */
static INLINE size_t rinf64_copy(struct rinf64 *s, uint8_t *o, size_t a,
uint32_t dist, uint32_t len)
{
if (dist <= a)
{
const uint8_t *f = o + a - dist;
if (dist >= len)
memmove(o + a, f, len);
else
{
uint32_t k;
for (k = 0; k < len; k++)
o[a + k] = f[k];
}
return a + len;
}
{
/* the part before the out, from the ring, then on in the out */
uint32_t back = dist - (uint32_t)a;
uint32_t r = (s->rpos - back) & (RINF64_WINDOW - 1);
uint32_t k = 0;
for (; k < len && k < back; k++)
{
o[a + k] = s->ring[r];
r = (r + 1) & (RINF64_WINDOW - 1);
}
for (; k < len; k++)
o[a + k] = o[a + k - dist];
}
return a + len;
}
/* The last of @n octets decoded into @o, kept for the next call's
* matches */
static void rinf64_keep(struct rinf64 *s, const uint8_t *o, size_t n)
{
if (n >= RINF64_WINDOW)
{
memcpy(s->ring, o + n - RINF64_WINDOW, RINF64_WINDOW);
s->rpos = 0;
return;
}
while (n)
{
uint32_t room = RINF64_WINDOW - s->rpos;
uint32_t k = n < room ? (uint32_t)n : room;
memcpy(s->ring + s->rpos, o, k);
s->rpos = (s->rpos + k) & (RINF64_WINDOW - 1);
o += k;
n -= k;
}
}
int rinflate64_process(void *stream, const uint8_t *in, size_t in_len,
int final, size_t *consumed, uint8_t *out, size_t out_len,
size_t *wrote)
{
struct rinf64 *s = (struct rinf64*)stream;
size_t at = 0;
int ret = RDEFLATE_PROCESS_NEXT;
s->p = in;
s->end = in + in_len;
/* not the last input, and less of it than a header or a symbol may
* read: the caller brings more first */
#define RINF64_SHORT() (!final \
&& (size_t)(s->end - s->p) < RINFLATE64_LOOKAHEAD)
for (;;)
{
/* a literal, or a match, the out filled up before */
if (s->held >= 0)
{
if (at == out_len)
break;
out[at++] = (uint8_t)s->held;
s->held = -1;
}
if (s->copy_len)
{
uint32_t k = out_len - at < s->copy_len
? (uint32_t)(out_len - at) : s->copy_len;
at = rinf64_copy(s, out, at, s->copy_dist, k);
s->copy_len -= k;
if (s->copy_len)
break;
}
if (s->state == RINF64_DONE)
{
ret = RDEFLATE_PROCESS_END;
break;
}
if (s->state == RINF64_HEADER)
{
uint32_t type;
if (s->last)
{
s->state = RINF64_DONE;
continue;
}
if (RINF64_SHORT())
break;
s->last = (int)rinf64_bits(s, 1);
type = rinf64_bits(s, 2);
if (type == 0)
{
/* from the next octet boundary, a length and its
* complement, then that many octets */
uint32_t len, nlen;
s->bits >>= s->n & 7;
s->n -= s->n & 7;
len = rinf64_bits(s, 16);
nlen = rinf64_bits(s, 16);
if ((len ^ 0xffffu) != nlen)
goto error;
s->stored_left = len;
s->state = RINF64_STORED;
}
else if (type == 3 || rinf64_tables(s, type == 2))
goto error;
else
s->state = RINF64_SYMBOLS;
if (s->over * 8 > s->n)
goto error; /* ran past the input */
continue;
}
if (s->state == RINF64_STORED)
{
/* what the bit buffer holds first, then the input itself */
while (s->stored_left && s->n >= 8 && at < out_len)
{
out[at++] = (uint8_t)s->bits;
s->bits >>= 8;
s->n -= 8;
s->stored_left--;
}
if (s->stored_left && at < out_len)
{
size_t k = (size_t)(s->end - s->p);
if (k > s->stored_left)
k = s->stored_left;
if (k > out_len - at)
k = out_len - at;
memcpy(out + at, s->p, k);
s->p += k;
at += k;
s->stored_left -= (uint32_t)k;
if (s->stored_left && s->p == s->end)
{
if (final)
goto error;
break;
}
}
if (s->stored_left)
break;
s->state = RINF64_HEADER;
continue;
}
/* a coded block's symbols */
{
/* where the input runs short of a symbol's worth, unless it
* is the last */
uintptr_t lim = final ? (uintptr_t)-1
: (uintptr_t)s->end - RINFLATE64_LOOKAHEAD;
uint64_t before = s->total;
for (;;)
{
int sym;
if ((uintptr_t)s->p > lim)
goto suspend;
if ((sym = rinf64_decode_sym(s, &s->lit)) < 0)
goto error;
if (sym < 256)
{
if (at < out_len)
{
out[at++] = (uint8_t)sym;
continue;
}
s->held = sym;
goto suspend;
}
if (sym == 256)
break;
{
uint32_t len, dist, room;
int dsym;
sym -= 257;
if (sym >= 29)
goto error;
len = rinf64_len_base[sym] + rinf64_bits(s, rinf64_len_extra[sym]);
if ((dsym = rinf64_decode_sym(s, &s->dist)) < 0 || dsym >= 32)
goto error;
dist = rinf64_dist_base[dsym] + rinf64_bits(s, rinf64_dist_extra[dsym]);
if (dist <= at && len <= out_len - at)
{
/* within the out, and room for it: as a whole
* stream decoded at once always is */
uint8_t *o = out + at;
const uint8_t *f = o - dist;
if (dist >= len)
memmove(o, f, len);
else
{
uint32_t k;
for (k = 0; k < len; k++)
o[k] = f[k];
}
at += len;
continue;
}
if ((uint64_t)dist > before + at)
goto error;
room = out_len - at < len ? (uint32_t)(out_len - at) : len;
at = rinf64_copy(s, out, at, dist, room);
if (room < len)
{
s->copy_len = len - room;
s->copy_dist = dist;
goto suspend;
}
}
}
}
if (s->over * 8 > s->n)
goto error;
s->state = RINF64_HEADER;
}
suspend:
#undef RINF64_SHORT
if (s->over * 8 > s->n)
goto error;
/* the final input, used up with the stream not done */
if ( ret == RDEFLATE_PROCESS_NEXT && final && at < out_len
&& s->p == s->end && s->state != RINF64_DONE
&& !s->copy_len && s->held < 0)
goto error;
rinf64_keep(s, out, at);
s->total += at;
if (consumed)
*consumed = (size_t)(s->p - in);
if (wrote)
*wrote = at;
return ret;
error:
if (consumed)
*consumed = (size_t)(s->p - in);
if (wrote)
*wrote = at;
return RDEFLATE_PROCESS_ERROR;
}
int rinflate64_decode(uint8_t *dst, size_t dst_len,
const uint8_t *src, size_t src_len, size_t *wrote)
{
void *z = rinflate64_new();
size_t got = 0;
int ret;
if (wrote)
*wrote = 0;
if (!z)
return RDEFLATE_PROCESS_ERROR;
ret = rinflate64_process(z, src, src_len, 1, NULL, dst, dst_len, &got);
/* more than the buffer holds */
if (ret == RDEFLATE_PROCESS_NEXT)
ret = RDEFLATE_PROCESS_ERROR;
if (ret == RDEFLATE_PROCESS_END && wrote)
*wrote = got;
rinflate64_free(z);
return ret;
}
+782
View File
@@ -0,0 +1,782 @@
/* Copyright (C) 2010-2026 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (encoding_rbzip2.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* bzip2 stream decoding; see <encodings/rbzip2.h> for the format. */
#include <stdlib.h>
#include <string.h>
#include <retro_inline.h>
#include <retro_endianness.h>
#include <encodings/rbzip2.h>
#define RBZ_GROUPS_MAX 6
#define RBZ_ALPHA_MAX 258 /* 256 octets, two run symbols */
#define RBZ_SEL_MAX 18002 /* 900 000 / 50, rounded up, and one */
#define RBZ_LEN_MAX 20
#define RBZ_LUT_BITS 10 /* codes this short decode in one look */
#define RBZ_GROUP_SIZE 50 /* symbols coded with one table */
/* A Huffman table, canonical: codes of one length are consecutive and
* in symbol order, each length's after the shorter ones'. */
struct rbz_table
{
uint16_t lut[1 << RBZ_LUT_BITS]; /* symbol << 5 | length; 0: longer */
uint16_t perm[RBZ_ALPHA_MAX]; /* symbols by length, then value */
uint16_t count[RBZ_LEN_MAX + 1]; /* codes of each length */
unsigned max_len;
};
/* The input, read most significant bit first through a 64-bit window.
* Past the end it reads zeros, counted in @over: a stream is short when
* more of them were taken than the window still holds. */
struct rbz_in
{
const uint8_t *p;
const uint8_t *end;
uint64_t buf;
size_t over;
/* input given in two pieces, the second where a ring buffer wraps:
* where it is until p reaches it, then NULL */
const uint8_t *next;
const uint8_t *next_end;
/* what was taken before these pieces, and where they start */
uint64_t base;
const uint8_t *first;
const uint8_t *second;
size_t first_len;
unsigned n;
};
/* Where a block's output goes: a buffer of fixed size, or one grown
* as it fills. */
struct rbz_out
{
uint8_t *dst;
size_t cap;
size_t at;
int grow;
};
struct rbzip2
{
/* the stream in hand, between rbzip2_step() calls */
struct rbz_in in;
struct rbz_out out;
uint32_t *tt; /* a block, then each entry's next */
uint32_t *inv; /* each entry's previous */
uint8_t *raw; /* the block untransformed */
uint8_t *own; /* a block at a time: where it goes */
size_t own_cap;
int blocks; /* rbzip2_begin_blocks() bound it */
uint32_t max_block;
uint32_t combined; /* the block CRCs so far */
uint32_t tt_cap;
uint32_t crc[8][256]; /* CRC-32, MSB first, eight at a time */
uint32_t unz[256]; /* each octet's count in the block */
struct rbz_table table[RBZ_GROUPS_MAX];
uint8_t len[RBZ_GROUPS_MAX][RBZ_ALPHA_MAX];
uint8_t selector[RBZ_SEL_MAX];
};
static INLINE uint64_t rbz_be64(const uint8_t *p)
{
uint64_t v;
memcpy(&v, p, 8);
return swap_if_little64(v);
}
/* The bit buffer topped up once under the 24 bits the most any one
* read takes: as many whole octets as it holds in one load where eight
* are left, else one at a time, past the end of the first piece onto
* the second, which is the one test the end of the input already
* costs */
#define RBZ_FILL(in, p, end, buf, n, over) \
if ((n) < 24) \
{ \
if ((size_t)((end) - (p)) >= 8) \
{ \
unsigned c_ = (64 - (n)) >> 3; \
(buf) = (((buf) << 1) << (c_ * 8 - 1)) \
| (rbz_be64(p) >> (64 - c_ * 8)); \
(p) += c_; \
(n) += c_ * 8; \
} \
else \
while ((n) <= 56) \
{ \
(buf) = ((buf) << 8) | ((p) < (end) ? *(p)++ \
: (in)->next ? ((p) = (in)->next, \
(end) = (in)->next_end, (in)->next = NULL, *(p)++) \
: ((over)++, 0u)); \
(n) += 8; \
} \
}
/* @k octets wide or less, 1 <= k <= 24 */
static uint32_t rbz_bits(struct rbz_in *in, unsigned k)
{
RBZ_FILL(in, in->p, in->end, in->buf, in->n, in->over)
in->n -= k;
return (uint32_t)(in->buf >> in->n) & ((1u << k) - 1);
}
static int rbz_short(const struct rbz_in *in)
{
return in->over * 8 > in->n;
}
rbzip2_t *rbzip2_new(void)
{
rbzip2_t *bz = (rbzip2_t*)calloc(1, sizeof(*bz));
unsigned i, j;
if (!bz)
return NULL;
for (i = 0; i < 256; i++)
{
uint32_t c = (uint32_t)i << 24;
for (j = 0; j < 8; j++)
c = (c & 0x80000000u) ? (c << 1) ^ 0x04c11db7u : c << 1;
bz->crc[0][i] = c;
}
for (j = 1; j < 8; j++)
for (i = 0; i < 256; i++)
bz->crc[j][i] = (bz->crc[j - 1][i] << 8)
^ bz->crc[0][bz->crc[j - 1][i] >> 24];
return bz;
}
void rbzip2_free(rbzip2_t *bz)
{
if (!bz)
return;
free(bz->tt);
free(bz->inv);
free(bz->raw);
free(bz->own);
free(bz);
}
/* The table for code lengths @len over @alpha symbols. Codes may leave
* some of the code space unused, which decodes as an error, but none
* may claim space another already has. */
static int rbz_table_build(struct rbz_table *t, const uint8_t *len,
unsigned alpha)
{
uint16_t offs[RBZ_LEN_MAX + 2];
unsigned i, l;
int32_t left = 1;
uint32_t code = 0;
unsigned at = 0;
memset(t->count, 0, sizeof(t->count));
for (i = 0; i < alpha; i++)
t->count[len[i]]++;
t->max_len = 0;
for (l = 1; l <= RBZ_LEN_MAX; l++)
{
left = (left << 1) - t->count[l];
if (left < 0)
return RBZIP2_ERROR_DATA;
if (t->count[l])
t->max_len = l;
}
offs[1] = 0;
for (l = 1; l <= RBZ_LEN_MAX; l++)
offs[l + 1] = (uint16_t)(offs[l] + t->count[l]);
for (i = 0; i < alpha; i++)
t->perm[offs[len[i]]++] = (uint16_t)i;
/* codes up to RBZ_LUT_BITS long, every window they begin */
memset(t->lut, 0, sizeof(t->lut));
for (l = 1; l <= RBZ_LUT_BITS; l++)
{
unsigned c;
for (c = 0; c < t->count[l]; c++, code++, at++)
{
unsigned span = 1u << (RBZ_LUT_BITS - l);
unsigned first = code << (RBZ_LUT_BITS - l);
uint16_t e = (uint16_t)((t->perm[at] << 5) | l);
unsigned j;
for (j = 0; j < span; j++)
t->lut[first + j] = e;
}
code <<= 1;
}
return RBZIP2_OK;
}
/* A code longer than the table looks at: @peek holds the next max_len
* bits. Returns the symbol, its length in *@used, or -1. */
static int rbz_slow(const struct rbz_table *t, uint32_t peek,
unsigned *used)
{
uint32_t code = 0;
uint32_t first = 0;
unsigned index = 0;
unsigned l;
for (l = 1; l <= t->max_len; l++)
{
uint32_t cnt = t->count[l];
code |= (peek >> (t->max_len - l)) & 1;
if (code - first < cnt)
{
*used = l;
return t->perm[index + code - first];
}
index += cnt;
first = (first + cnt) << 1;
code <<= 1;
}
return -1;
}
/* The block CRC of @len octets: eight at a time, the table for each
* octet giving its effect that many octets on. */
static uint32_t rbz_crc(const rbzip2_t *bz, const uint8_t *p, size_t len)
{
uint32_t v = 0xffffffffu;
for (; len >= 8; p += 8, len -= 8)
{
uint32_t a = v ^ (((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16)
| ((uint32_t)p[2] << 8) | p[3]);
v = bz->crc[7][a >> 24] ^ bz->crc[6][(a >> 16) & 0xff]
^ bz->crc[5][(a >> 8) & 0xff] ^ bz->crc[4][a & 0xff]
^ bz->crc[3][p[4]] ^ bz->crc[2][p[5]]
^ bz->crc[1][p[6]] ^ bz->crc[0][p[7]];
}
for (; len; p++, len--)
v = (v << 8) ^ bz->crc[0][(v >> 24) ^ *p];
return ~v;
}
/* Room for @n more octets */
static int rbz_room(struct rbz_out *o, size_t n)
{
size_t cap;
uint8_t *d;
if (n <= o->cap - o->at)
return RBZIP2_OK;
if (!o->grow)
return RBZIP2_ERROR_SPACE;
cap = o->cap + o->cap / 2 + n + 65536;
if (cap < o->cap || !(d = (uint8_t*)realloc(o->dst, cap + 1)))
return RBZIP2_ERROR_MEM;
o->dst = d;
o->cap = cap;
return RBZIP2_OK;
}
/* One block, after its magic: decoded and appended to @out. *@crc
* receives the block's stored CRC once it has been checked. */
static int rbz_block(rbzip2_t *bz, struct rbz_in *in, uint32_t max_block,
struct rbz_out *out, uint32_t *crc)
{
uint8_t mtf[256];
uint8_t pos[RBZ_GROUPS_MAX];
uint32_t stored, orig, used, i, j;
uint32_t nblock = 0, n_sel, n_groups, n_in_use = 0, alpha, eob;
uint32_t run = 0, run_bit = 1, sel = 0, left = 0, nsym = 0;
uint32_t *tt = bz->tt;
const struct rbz_table *t = NULL;
const uint8_t *p, *end;
uint64_t buf;
unsigned n;
size_t over;
int e;
stored = rbz_bits(in, 16) << 16;
stored |= rbz_bits(in, 16);
if (rbz_bits(in, 1))
return RBZIP2_ERROR_DATA; /* randomised */
orig = rbz_bits(in, 24);
/* the octets the block uses, sixteen at a time */
used = rbz_bits(in, 16);
for (i = 0; i < 16; i++)
if (used & (0x8000u >> i))
{
uint32_t m = rbz_bits(in, 16);
for (j = 0; j < 16; j++)
if (m & (0x8000u >> j))
mtf[n_in_use++] = (uint8_t)(i * 16 + j);
}
if (!n_in_use)
return RBZIP2_ERROR_DATA;
alpha = n_in_use + 2;
eob = n_in_use + 1;
n_groups = rbz_bits(in, 3);
n_sel = rbz_bits(in, 15);
if (n_groups < 2 || n_groups > RBZ_GROUPS_MAX || !n_sel)
return RBZIP2_ERROR_DATA;
/* each table's selector, move-to-front coded in unary */
for (i = 0; i < n_groups; i++)
pos[i] = (uint8_t)i;
for (i = 0; i < n_sel; i++)
{
uint8_t v;
j = 0;
while (rbz_bits(in, 1))
if (++j >= n_groups)
return RBZIP2_ERROR_DATA;
v = pos[j];
for (; j > 0; j--)
pos[j] = pos[j - 1];
pos[0] = v;
if (i < RBZ_SEL_MAX)
bz->selector[i] = v;
}
if (n_sel > RBZ_SEL_MAX)
n_sel = RBZ_SEL_MAX;
/* each table's code lengths, as steps from a five-bit start */
for (i = 0; i < n_groups; i++)
{
uint32_t cur = rbz_bits(in, 5);
for (j = 0; j < alpha; j++)
{
for (;;)
{
if (cur < 1 || cur > RBZ_LEN_MAX)
return RBZIP2_ERROR_DATA;
if (!rbz_bits(in, 1))
break;
cur = rbz_bits(in, 1) ? cur - 1 : cur + 1;
}
bz->len[i][j] = (uint8_t)cur;
}
if ((e = rbz_table_build(&bz->table[i], bz->len[i], alpha)) != RBZIP2_OK)
return e;
}
if (rbz_short(in))
return RBZIP2_ERROR_DATA;
/* the symbols: runs of rank 0, other ranks, the end of the block */
memset(bz->unz, 0, sizeof(bz->unz));
p = in->p;
end = in->end;
buf = in->buf;
n = in->n;
over = in->over;
for (;;)
{
uint32_t sym, ent;
if (!left)
{
if (sel >= n_sel)
return RBZIP2_ERROR_DATA;
t = &bz->table[bz->selector[sel++]];
left = RBZ_GROUP_SIZE;
}
left--;
nsym++;
RBZ_FILL(in, p, end, buf, n, over)
ent = t->lut[(uint32_t)(buf >> (n - RBZ_LUT_BITS)) & ((1u << RBZ_LUT_BITS) - 1)];
if (ent)
{
n -= ent & 31;
sym = ent >> 5;
}
else
{
unsigned l = 0;
int s = rbz_slow(t, (uint32_t)(buf >> (n - t->max_len))
& ((1u << t->max_len) - 1), &l);
if (s < 0)
return RBZIP2_ERROR_DATA;
n -= l;
sym = (uint32_t)s;
}
if (sym <= 1)
{
/* RUNA adds one at this digit, RUNB two */
if (run_bit > max_block)
return RBZIP2_ERROR_DATA;
run += run_bit << sym;
run_bit <<= 1;
continue;
}
if (run)
{
uint8_t c = mtf[0];
if (run > max_block - nblock)
return RBZIP2_ERROR_DATA;
bz->unz[c] += run;
while (run--)
tt[nblock++] = c;
run = 0;
run_bit = 1;
}
if (sym == eob)
break;
if (sym >= alpha || nblock >= max_block)
return RBZIP2_ERROR_DATA;
{
uint32_t r = sym - 1;
uint8_t c = mtf[r];
memmove(mtf + 1, mtf, r);
mtf[0] = c;
bz->unz[c]++;
tt[nblock++] = c;
}
}
in->p = p;
in->end = end;
in->buf = buf;
in->n = n;
in->over = over;
if (rbz_short(in) || orig >= nblock)
return RBZIP2_ERROR_DATA;
/* the inverse transform: each entry linked to the one after it in
* the block. Where the block is mostly long runs of rank 0 its walk
* stays near itself; otherwise each step is a load from anywhere,
* so each entry is linked to the one before it as well and the
* block is walked from both ends at once, one walk's loads waiting
* alongside the other's. */
{
uint32_t sum = 0;
int both = nsym > nblock / 8;
for (i = 0; i < 256; i++)
{
uint32_t c = bz->unz[i];
bz->unz[i] = sum;
sum += c;
}
if (both)
{
uint32_t *inv = bz->inv;
uint8_t *raw = bz->raw;
uint32_t f, b, k;
for (i = 0; i < nblock; i++)
{
uint32_t c = tt[i] & 0xff;
uint32_t to = bz->unz[c]++;
tt[to] |= i << 8;
inv[i] = (to << 8) | c;
}
/* the walk from orig comes round to orig last */
f = tt[orig] >> 8;
b = orig;
for (i = 0, k = nblock - 1; i < k; i++, k--)
{
uint32_t ef = tt[f], eb = inv[b];
raw[i] = (uint8_t)ef;
raw[k] = (uint8_t)eb;
f = ef >> 8;
b = eb >> 8;
}
if (i == k)
raw[i] = (uint8_t)tt[f];
}
else
for (i = 0; i < nblock; i++)
tt[bz->unz[tt[i] & 0xff]++] |= i << 8;
/* and out, undoing the runs of four and a count */
{
const uint8_t *raw = bz->raw;
size_t from = out->at;
uint8_t *d = out->dst;
size_t at = out->at;
size_t cap = out->cap;
uint32_t tp = tt[orig] >> 8;
uint32_t last = 0x100; /* no octet */
unsigned rep = 0;
uint32_t k = 0;
uint32_t c;
/* the next octet of the block, from the walk done or walking */
#define RBZ_NEXT() \
if (both) \
c = raw[k]; \
else \
{ \
tp = tt[tp]; \
c = tp & 0xff; \
tp >>= 8; \
} \
k++
while (k < nblock)
{
RBZ_NEXT();
if (at >= cap)
{
out->at = at;
if ((e = rbz_room(out, 1)) != RBZIP2_OK)
return e;
d = out->dst;
cap = out->cap;
}
d[at++] = (uint8_t)c;
if (c != last)
{
last = c;
rep = 1;
continue;
}
if (++rep < 4)
continue;
/* four alike: the next octet counts more of them */
rep = 0;
if (k == nblock)
break;
RBZ_NEXT();
if (c > cap - at)
{
out->at = at;
if ((e = rbz_room(out, c)) != RBZIP2_OK)
return e;
d = out->dst;
cap = out->cap;
}
memset(d + at, (int)last, c);
at += c;
}
#undef RBZ_NEXT
out->at = at;
if (rbz_crc(bz, out->dst + from, out->at - from) != stored)
return RBZIP2_ERROR_DATA;
}
}
*crc = stored;
return RBZIP2_OK;
}
/* The stream at @src: its header read and the block buffers sized for
* it, ready for its first block */
static int rbz_start(rbzip2_t *bz, const uint8_t *src, size_t src_len)
{
uint32_t max_block;
if ( !bz || src_len < 4
|| src[0] != 'B' || src[1] != 'Z' || src[2] != 'h'
|| src[3] < '1' || src[3] > '9')
return RBZIP2_ERROR_DATA;
max_block = (uint32_t)(src[3] - '0') * 100000u;
if (bz->tt_cap < max_block)
{
free(bz->tt);
free(bz->inv);
free(bz->raw);
bz->tt = (uint32_t*)malloc(max_block * sizeof(uint32_t));
bz->inv = (uint32_t*)malloc(max_block * sizeof(uint32_t));
bz->raw = (uint8_t*)malloc(max_block);
bz->tt_cap = max_block;
if (!bz->tt || !bz->inv || !bz->raw)
{
bz->tt_cap = 0;
return RBZIP2_ERROR_MEM;
}
}
bz->max_block = max_block;
bz->combined = 0;
bz->in.p = src + 4;
bz->in.end = src + src_len;
bz->in.buf = 0;
bz->in.n = 0;
bz->in.over = 0;
bz->in.next = NULL;
bz->in.next_end = NULL;
bz->in.base = 0;
bz->in.first = src;
bz->in.first_len = src_len;
bz->in.second = NULL;
return RBZIP2_OK;
}
/* How much of the input has been taken, what is held in the bit buffer
* with it */
static uint64_t rbz_taken(const struct rbz_in *in)
{
if (in->second && !in->next)
return in->base + in->first_len + (uint64_t)(in->p - in->second);
return in->base + (uint64_t)(in->p - in->first);
}
/* The next block, or the end of the stream and its CRC */
static int rbz_next(rbzip2_t *bz)
{
uint32_t hi = rbz_bits(&bz->in, 24);
uint32_t lo = rbz_bits(&bz->in, 24);
if (hi == 0x314159u && lo == 0x265359u)
{
uint32_t crc;
int e = rbz_block(bz, &bz->in, bz->max_block, &bz->out, &crc);
if (e != RBZIP2_OK)
return e;
bz->combined = ((bz->combined << 1) | (bz->combined >> 31)) ^ crc;
return RBZIP2_PENDING;
}
if (hi == 0x177245u && lo == 0x385090u)
{
uint32_t want = rbz_bits(&bz->in, 16) << 16;
want |= rbz_bits(&bz->in, 16);
if (rbz_short(&bz->in) || want != bz->combined)
return RBZIP2_ERROR_DATA;
return RBZIP2_OK;
}
return RBZIP2_ERROR_DATA;
}
/* The rest of the stream rbz_start() began */
static int rbz_stream(rbzip2_t *bz)
{
int e;
while ((e = rbz_next(bz)) == RBZIP2_PENDING) { }
return e;
}
int rbzip2_decode(rbzip2_t *bz, uint8_t *dst, size_t dst_len,
const uint8_t *src, size_t src_len, size_t *wrote)
{
int e = rbzip2_begin(bz, dst, dst_len, src, src_len);
if (e == RBZIP2_OK)
e = rbz_stream(bz);
if (wrote)
*wrote = e == RBZIP2_OK ? bz->out.at : 0;
return e;
}
int rbzip2_begin(rbzip2_t *bz, uint8_t *dst, size_t dst_len,
const uint8_t *src, size_t src_len)
{
if (!bz)
return RBZIP2_ERROR_DATA;
bz->blocks = 0;
bz->out.dst = dst;
bz->out.cap = dst_len;
bz->out.at = 0;
bz->out.grow = 0;
return rbz_start(bz, src, src_len);
}
int rbzip2_begin_blocks(rbzip2_t *bz, const uint8_t *src, size_t src_len)
{
if (!bz)
return RBZIP2_ERROR_DATA;
bz->blocks = 1;
return rbz_start(bz, src, src_len);
}
int rbzip2_begin_ring(rbzip2_t *bz, const uint8_t *a, size_t a_len,
const uint8_t *b, size_t b_len)
{
int e;
if (!bz)
return RBZIP2_ERROR_DATA;
bz->blocks = 1;
if ((e = rbz_start(bz, a, a_len)) != RBZIP2_OK)
return e;
if (b_len)
{
bz->in.second = b;
bz->in.next = b;
bz->in.next_end = b + b_len;
}
return RBZIP2_OK;
}
void rbzip2_input(rbzip2_t *bz, const uint8_t *a, size_t a_len,
const uint8_t *b, size_t b_len)
{
if (!bz)
return;
bz->in.base = rbz_taken(&bz->in);
bz->in.p = a;
bz->in.end = a + a_len;
bz->in.first = a;
bz->in.first_len = a_len;
bz->in.second = b_len ? b : NULL;
bz->in.next = b_len ? b : NULL;
bz->in.next_end = b + b_len;
}
uint64_t rbzip2_taken(const rbzip2_t *bz)
{
return bz ? rbz_taken(&bz->in) : 0;
}
int rbzip2_step_block(rbzip2_t *bz, const uint8_t **block, size_t *len)
{
int e;
*block = NULL;
*len = 0;
if (!bz || !bz->blocks)
return RBZIP2_ERROR_DATA;
/* each block from the start of the decoder's own buffer, which
* grows to the largest */
bz->out.dst = bz->own;
bz->out.cap = bz->own_cap;
bz->out.at = 0;
bz->out.grow = 1;
e = rbz_next(bz);
bz->own = bz->out.dst;
bz->own_cap = bz->out.cap;
if (e == RBZIP2_PENDING)
{
*block = bz->own;
*len = bz->out.at;
}
return e;
}
int rbzip2_step(rbzip2_t *bz, size_t *wrote)
{
int e = rbz_next(bz);
if (wrote)
*wrote = bz->out.at;
return e;
}
int rbzip2_decode_alloc(rbzip2_t *bz, const uint8_t *src, size_t src_len,
uint8_t **dst, size_t *wrote)
{
int e;
*dst = NULL;
*wrote = 0;
if (!bz)
return RBZIP2_ERROR_DATA;
bz->blocks = 0;
bz->out.dst = NULL;
bz->out.cap = 0;
bz->out.at = 0;
bz->out.grow = 1;
/* about what bzip2 makes of most data, to start */
if ( (e = rbz_room(&bz->out, src_len * 4)) == RBZIP2_OK
&& (e = rbz_start(bz, src, src_len)) == RBZIP2_OK)
e = rbz_stream(bz);
if (e != RBZIP2_OK)
{
free(bz->out.dst);
bz->out.dst = NULL;
return e;
}
*dst = bz->out.dst;
*wrote = bz->out.at;
bz->out.dst = NULL;
return RBZIP2_OK;
}
+465 -119
View File
@@ -55,6 +55,8 @@
* 256, and the single-segment case where
* the content size doubles as the window.
* Blocks (3.1.1.2) raw, RLE and compressed.
* Checksum (3.1.1) a frame's XXH64, taken over each block
* as it is decoded, where it lies.
* Reverse bitstream (4) the backwards, MSB-first reader the
* entropy streams use, and the padding
* marker in the final byte.
@@ -73,10 +75,6 @@
*
* Not built:
*
* Checksum a frame's XXH64 is skipped, not
* verified. A caller expecting a stated
* checksum to be checked does not get
* that.
* Huffman literal encoding literals are emitted raw. Legal, and
* costs ratio.
* Encoder refinements one hash with no chain, so only the
@@ -156,6 +154,7 @@
#include <retro_inline.h>
#include <compat/intrinsics.h>
#include <encodings/rzstd.h>
#include <encodings/xxh64.h>
/* The decode loops spend much of their time on variable shifts, which
* BMI2 does in one flagless instruction where baseline x86-64 takes
@@ -1452,6 +1451,12 @@ RZSTD_BODY_INLINE int rzstd_huf_decode_body(const rzstd_huf_t *huf, const uint8_
* The stores were a quarter of this loop. */
{
uint64_t w0 = 0, w1 = 0, w2 = 0, w3 = 0;
/* where in the word the i-th byte in memory is */
#ifdef RZSTD_BIG_ENDIAN
#define RZSTD_LANE(i) (56 - (i) * 8)
#else
#define RZSTD_LANE(i) ((i) * 8)
#endif
for (i = 0; i < 5; i++)
{
@@ -1460,10 +1465,10 @@ RZSTD_BODY_INLINE int rzstd_huf_decode_body(const rzstd_huf_t *huf, const uint8_
uint32_t e2 = ent[f[2].bits >> sh];
uint32_t e3 = ent[f[3].bits >> sh];
w0 |= (uint64_t)(uint8_t)(e0 >> 8) << (i * 8);
w1 |= (uint64_t)(uint8_t)(e1 >> 8) << (i * 8);
w2 |= (uint64_t)(uint8_t)(e2 >> 8) << (i * 8);
w3 |= (uint64_t)(uint8_t)(e3 >> 8) << (i * 8);
w0 |= (uint64_t)(uint8_t)(e0 >> 8) << RZSTD_LANE(i);
w1 |= (uint64_t)(uint8_t)(e1 >> 8) << RZSTD_LANE(i);
w2 |= (uint64_t)(uint8_t)(e2 >> 8) << RZSTD_LANE(i);
w3 |= (uint64_t)(uint8_t)(e3 >> 8) << RZSTD_LANE(i);
f[0].bits <<= (e0 & 63);
f[1].bits <<= (e1 & 63);
@@ -1480,6 +1485,7 @@ RZSTD_BODY_INLINE int rzstd_huf_decode_body(const rzstd_huf_t *huf, const uint8_
memcpy(dst + pos[1], &w1, 8);
memcpy(dst + pos[2], &w2, 8);
memcpy(dst + pos[3], &w3, 8);
#undef RZSTD_LANE
}
pos[0] += 5; pos[1] += 5; pos[2] += 5; pos[3] += 5;
rzstd_fast_reload(&f[0]);
@@ -2180,6 +2186,10 @@ static int rzstd_seq_table(rzstd_fse_t *fse, rzstd_fse_entry_t *storage,
#endif
/* Decodes a block's sequences and executes them into @dst.
*
* The output before @dst, when the window is a ring and has come round
* to its start, is the @ext_len octets before @ext_end: a match that
* reaches back past @dst is copied from there, then on from @dst.
*
* The three offsets most recently used are remembered, and an offset
* code of 1, 2 or 3 names one of them rather than a distance -- with
@@ -2189,7 +2199,8 @@ static int rzstd_seq_table(rzstd_fse_t *fse, rzstd_fse_entry_t *storage,
RZSTD_BODY_INLINE int rzstd_decode_sequences_body(rzstd_seq_tables_t *tab,
const uint8_t *src, size_t src_len,
const uint8_t *literals, size_t lit_len, size_t lit_readable,
uint8_t *dst, size_t dst_len, size_t *dst_at, uint32_t *repeat)
uint8_t *dst, size_t dst_len, size_t *dst_at, uint32_t *repeat,
const uint8_t *ext_end, size_t ext_len)
{
rzstd_rbits_t bits;
size_t nseq;
@@ -2440,13 +2451,17 @@ RZSTD_BODY_INLINE int rzstd_decode_sequences_body(rzstd_seq_tables_t *tab,
}
else
{
size_t back;
if (!offset)
return RZ_DATA;
if (lit_at + lit_run > lit_len)
return RZ_DATA;
if (out + lit_run + match_len > dst_len)
return RZ_DATA;
if ((size_t)offset > out + lit_run)
back = (size_t)offset > out + lit_run
? (size_t)offset - (out + lit_run) : 0;
if (back > ext_len)
return RZ_DATA;
/* Falling off the fast path does not mean copying narrowly.
@@ -2463,8 +2478,20 @@ RZSTD_BODY_INLINE int rzstd_decode_sequences_body(rzstd_seq_tables_t *tab,
memcpy(dst + out, literals + lit_at, lit_run);
out += lit_run;
lit_at += lit_run;
rzstd_match_copy(dst + out, dst + out - offset, match_len,
offset, out + match_len + RZSTD_COPY_SLACK <= dst_len);
if (back)
{
/* from the end of the ring, then from its start */
size_t k = back < match_len ? back : match_len;
memcpy(dst + out, ext_end - back, k);
out += k;
match_len -= (uint32_t)k;
if (match_len)
rzstd_match_copy(dst + out, dst, match_len, (uint32_t)out,
out + match_len + RZSTD_COPY_SLACK <= dst_len);
}
else
rzstd_match_copy(dst + out, dst + out - offset, match_len,
offset, out + match_len + RZSTD_COPY_SLACK <= dst_len);
out += match_len;
}
@@ -2489,41 +2516,45 @@ RZSTD_BODY_INLINE int rzstd_decode_sequences_body(rzstd_seq_tables_t *tab,
static int rzstd_decode_sequences_sse(rzstd_seq_tables_t *tab,
const uint8_t *src, size_t src_len,
const uint8_t *literals, size_t lit_len, size_t lit_readable,
uint8_t *dst, size_t dst_len, size_t *dst_at, uint32_t *repeat)
uint8_t *dst, size_t dst_len, size_t *dst_at, uint32_t *repeat,
const uint8_t *ext_end, size_t ext_len)
{
return rzstd_decode_sequences_body(tab, src, src_len, literals, lit_len,
lit_readable, dst, dst_len, dst_at, repeat);
lit_readable, dst, dst_len, dst_at, repeat, ext_end, ext_len);
}
RZSTD_TARGET_BMI2
static int rzstd_decode_sequences_bmi2(rzstd_seq_tables_t *tab,
const uint8_t *src, size_t src_len,
const uint8_t *literals, size_t lit_len, size_t lit_readable,
uint8_t *dst, size_t dst_len, size_t *dst_at, uint32_t *repeat)
uint8_t *dst, size_t dst_len, size_t *dst_at, uint32_t *repeat,
const uint8_t *ext_end, size_t ext_len)
{
return rzstd_decode_sequences_body(tab, src, src_len, literals, lit_len,
lit_readable, dst, dst_len, dst_at, repeat);
lit_readable, dst, dst_len, dst_at, repeat, ext_end, ext_len);
}
static int rzstd_decode_sequences(rzstd_seq_tables_t *tab,
const uint8_t *src, size_t src_len,
const uint8_t *literals, size_t lit_len, size_t lit_readable,
uint8_t *dst, size_t dst_len, size_t *dst_at, uint32_t *repeat)
uint8_t *dst, size_t dst_len, size_t *dst_at, uint32_t *repeat,
const uint8_t *ext_end, size_t ext_len)
{
if (rzstd_cpu_bmi2())
return rzstd_decode_sequences_bmi2(tab, src, src_len, literals, lit_len,
lit_readable, dst, dst_len, dst_at, repeat);
lit_readable, dst, dst_len, dst_at, repeat, ext_end, ext_len);
return rzstd_decode_sequences_sse(tab, src, src_len, literals, lit_len,
lit_readable, dst, dst_len, dst_at, repeat);
lit_readable, dst, dst_len, dst_at, repeat, ext_end, ext_len);
}
#else
static int rzstd_decode_sequences(rzstd_seq_tables_t *tab,
const uint8_t *src, size_t src_len,
const uint8_t *literals, size_t lit_len, size_t lit_readable,
uint8_t *dst, size_t dst_len, size_t *dst_at, uint32_t *repeat)
uint8_t *dst, size_t dst_len, size_t *dst_at, uint32_t *repeat,
const uint8_t *ext_end, size_t ext_len)
{
return rzstd_decode_sequences_body(tab, src, src_len, literals, lit_len,
lit_readable, dst, dst_len, dst_at, repeat);
lit_readable, dst, dst_len, dst_at, repeat, ext_end, ext_len);
}
#endif
@@ -2550,10 +2581,6 @@ static void rzstd_read_block_header(const uint8_t *p,
out->size = v >> 3;
}
/* Decodes one frame into @dst, which must be large enough for what the
* frame states. Returns bytes written through @wrote and how much of
* @src the frame occupied through @used, so a caller holding several
* frames can walk them. */
/* What survives from one block to the next within a frame: the Huffman
* table a treeless block reuses, the three sequence tables a repeat
* mode reuses, and the recent offsets. */
@@ -2568,14 +2595,12 @@ typedef struct rzstd_frame_state
uint8_t literals[RZSTD_BLOCK_MAX + 128];
} rzstd_frame_state_t;
static int rzstd_decode_frame(const uint8_t *src, size_t src_len,
uint8_t *dst, size_t dst_len, size_t *wrote, size_t *used,
rzstd_frame_state_t *st)
/* The frame's header read and the state its blocks share set, ready
* for its first block; *@at is where that is. */
static int rzstd_frame_begin(const uint8_t *src, size_t src_len,
rzstd_frame_header_t *h, size_t *at, rzstd_frame_state_t *st)
{
rzstd_frame_header_t h;
size_t at = 0;
size_t out = 0;
int e;
int e;
/* Only the three flags need clearing.
*
@@ -2600,97 +2625,160 @@ static int rzstd_decode_frame(const uint8_t *src, size_t src_len,
st->repeat[1] = 4;
st->repeat[2] = 8;
if ((e = rzstd_read_frame_header(src, src_len, &h)) != RZ_OK)
if ((e = rzstd_read_frame_header(src, src_len, h)) != RZ_OK)
return e;
at = h.header_len;
*at = h->header_len;
return RZ_OK;
}
for (;;)
{
rzstd_block_header_t b;
/* The body of the block @b, its @avail octets at @src, decoded into
* @dst from *@out on, with the @ext_len octets before @ext_end before
* @dst; *@used is how many octets of @src it took */
static int rzstd_block_body(const rzstd_block_header_t *b,
const uint8_t *src, size_t avail, size_t *used, uint8_t *dst,
size_t dst_len, size_t *out, rzstd_frame_state_t *st,
const uint8_t *ext_end, size_t ext_len)
{
int e;
if (src_len < at + 3)
return RZ_TRUNCATED;
rzstd_read_block_header(src + at, &b);
at += 3;
if (b.type == RZSTD_BLOCK_RESERVED)
return RZ_DATA;
if (b.size > RZSTD_BLOCK_MAX)
return RZ_DATA;
switch (b.type)
{
case RZSTD_BLOCK_RAW:
/* The block's bytes are its content, uncompressed. */
if (src_len < at + b.size)
return RZ_TRUNCATED;
if (out + b.size > dst_len)
return RZ_DATA;
memcpy(dst + out, src + at, b.size);
out += b.size;
at += b.size;
break;
case RZSTD_BLOCK_RLE:
/* One byte stands for @size copies of itself, so the block
* occupies a single byte however long its content. */
if (src_len < at + 1)
return RZ_TRUNCATED;
if (out + b.size > dst_len)
return RZ_DATA;
memset(dst + out, src[at], b.size);
out += b.size;
at += 1;
break;
default:
{
/* Compressed: a literals section, then the sequences that
* weave it together with matches into earlier output. */
rzstd_literals_t lit;
size_t lit_used = 0;
size_t before = out;
if (src_len < at + b.size)
return RZ_TRUNCATED;
e = rzstd_read_literals(&lit, &st->huf, &st->huf_valid,
src + at, b.size, st->literals, sizeof(st->literals),
&lit_used);
if (e != RZ_OK)
return e;
e = rzstd_decode_sequences(&st->tables,
src + at + lit_used, b.size - lit_used,
lit.data, lit.size, lit.readable,
dst, dst_len, &out, st->repeat);
if (e != RZ_OK)
return e;
if (out - before > RZSTD_BLOCK_MAX)
return RZ_DATA;
at += b.size;
break;
}
}
if (b.last)
break;
}
/* Section 3.1.1: the optional checksum is the low four bytes of the
* frame content's XXH64. It is skipped rather than verified, which
* is noted because a caller may reasonably expect it to be checked
* when the frame says it is there. */
if (h.has_checksum)
{
if (src_len < at + 4)
return RZ_TRUNCATED;
at += 4;
}
if (h.has_content_size && out != h.content_size)
if (b->type == RZSTD_BLOCK_RESERVED)
return RZ_DATA;
if (b->size > RZSTD_BLOCK_MAX)
return RZ_DATA;
switch (b->type)
{
case RZSTD_BLOCK_RAW:
/* The block's bytes are its content, uncompressed. */
if (avail < b->size)
return RZ_TRUNCATED;
if (*out + b->size > dst_len)
return RZ_DATA;
memcpy(dst + *out, src, b->size);
*out += b->size;
*used = b->size;
break;
case RZSTD_BLOCK_RLE:
/* One byte stands for @size copies of itself, so the block
* occupies a single byte however long its content. */
if (avail < 1)
return RZ_TRUNCATED;
if (*out + b->size > dst_len)
return RZ_DATA;
memset(dst + *out, src[0], b->size);
*out += b->size;
*used = 1;
break;
default:
{
/* Compressed: a literals section, then the sequences that
* weave it together with matches into earlier output. */
rzstd_literals_t lit;
size_t lit_used = 0;
size_t before = *out;
if (avail < b->size)
return RZ_TRUNCATED;
e = rzstd_read_literals(&lit, &st->huf, &st->huf_valid,
src, b->size, st->literals, sizeof(st->literals),
&lit_used);
if (e != RZ_OK)
return e;
e = rzstd_decode_sequences(&st->tables,
src + lit_used, b->size - lit_used,
lit.data, lit.size, lit.readable,
dst, dst_len, out, st->repeat, ext_end, ext_len);
if (e != RZ_OK)
return e;
if (*out - before > RZSTD_BLOCK_MAX)
return RZ_DATA;
*used = b->size;
break;
}
}
return RZ_OK;
}
/* The block at *@at, decoded into @dst from *@out on, its matches
* reaching back into what is before that; *@last is set by the frame's
* last. */
static int rzstd_frame_block(const uint8_t *src, size_t src_len,
size_t *at, uint8_t *dst, size_t dst_len, size_t *out,
rzstd_frame_state_t *st, int *last, const uint8_t *ext_end,
size_t ext_len)
{
rzstd_block_header_t b;
size_t used = 0;
int e;
if (src_len < *at + 3)
return RZ_TRUNCATED;
rzstd_read_block_header(src + *at, &b);
*at += 3;
if ((e = rzstd_block_body(&b, src + *at, src_len - *at, &used, dst,
dst_len, out, st, ext_end, ext_len)) != RZ_OK)
return e;
*at += used;
*last = b.last;
return RZ_OK;
}
/* After the last block: the frame's end, and all of it, @total octets
* whose XXH64 @xs holds, what the header said. */
static int rzstd_frame_end(const uint8_t *src, size_t src_len,
const rzstd_frame_header_t *h, size_t *at, uint64_t total,
const xxh64_state_t *xs)
{
/* Section 3.1.1: the optional checksum is the low four bytes of the
* frame content's XXH64 */
if (h->has_checksum)
{
if (src_len < *at + 4)
return RZ_TRUNCATED;
if (rzstd_rd32(src + *at) != (uint32_t)xxh64_digest(xs))
return RZ_DATA;
*at += 4;
}
if (h->has_content_size && total != h->content_size)
return RZ_DATA;
return RZ_OK;
}
/* Decodes one frame into @dst, which must be large enough for what the
* frame states. Returns bytes written through @wrote and how much of
* @src the frame occupied through @used, so a caller holding several
* frames can walk them. */
static int rzstd_decode_frame(const uint8_t *src, size_t src_len,
uint8_t *dst, size_t dst_len, size_t *wrote, size_t *used,
rzstd_frame_state_t *st)
{
rzstd_frame_header_t h;
xxh64_state_t xs;
size_t at = 0;
size_t out = 0;
int last = 0;
int e;
if ((e = rzstd_frame_begin(src, src_len, &h, &at, st)) != RZ_OK)
return e;
xxh64_reset(&xs, 0);
while (!last)
{
size_t from = out;
if ((e = rzstd_frame_block(src, src_len, &at, dst, dst_len, &out,
st, &last, NULL, 0)) != RZ_OK)
return e;
/* while the block is still in cache */
if (h.has_checksum)
xxh64_update(&xs, dst + from, out - from);
}
if ((e = rzstd_frame_end(src, src_len, &h, &at, out, &xs)) != RZ_OK)
return e;
if (wrote)
*wrote = out;
@@ -2699,6 +2787,254 @@ static int rzstd_decode_frame(const uint8_t *src, size_t src_len,
return RZ_OK;
}
/* -------- a frame a block at a time -------- */
struct rzstd_blocks
{
rzstd_frame_state_t st;
rzstd_frame_header_t h;
xxh64_state_t xs; /* the checksum so far */
uint64_t total; /* octets decoded */
const uint8_t *src;
uint8_t *buf; /* the window, and room for a block */
size_t src_len;
size_t at; /* where in src the next block is */
size_t cap;
size_t window; /* how far back matches reach */
size_t out; /* where in buf the next block goes */
size_t ext; /* where the ring's last time round
ended, 0 before it has come round */
size_t want; /* given a piece at a time: the next */
rzstd_block_header_t next; /* the block that piece holds */
int done;
int stage; /* 0 a whole frame, 1 and 2 its header
in pieces, 3 its blocks */
uint8_t head[RZSTD_FRAME_HEADER_MAX + 3];
};
/* The frame header's length, from its first five octets */
static size_t rzstd_header_len(const uint8_t *p)
{
uint32_t desc = p[4];
uint32_t dict = desc & 3;
int one = (desc >> 5) & 1;
return 5 + (one ? 0 : 1) + (dict == 3 ? 4 : dict)
+ rzstd_fcs_bytes(desc >> 6, one);
}
/* The window buffer, for the header in @z->h: a ring of the window,
* two blocks and a wide copy's overrun. A block goes where it fits
* whole, at the start again once one would not; matches reaching back
* before the start are copied from the end. A single segment's window
* is all of it, which can be more than a descriptor may say, and never
* comes round. */
#define RZSTD_RING_SPARE (2 * RZSTD_BLOCK_MAX + RZSTD_COPY_SLACK)
/* A small window's ring comes round no more than once a megabyte: a
* match back into the end is copied in two */
#define RZSTD_RING_MIN (1024 * 1024)
static int rzstd_blocks_buffer(rzstd_blocks_t *z)
{
uint64_t window, cap;
window = z->h.single_segment ? z->h.content_size : z->h.window_size;
cap = window + RZSTD_RING_SPARE;
if (!z->h.single_segment && cap < RZSTD_RING_MIN)
cap = RZSTD_RING_MIN;
if ( window > (uint64_t)((size_t)-1) - RZSTD_RING_SPARE
|| !(z->buf = (uint8_t*)malloc((size_t)cap)))
return 0;
z->cap = (size_t)cap;
z->window = (size_t)window;
return 1;
}
static rzstd_blocks_t *rzstd_blocks_alloc(void)
{
rzstd_blocks_t *z = (rzstd_blocks_t*)malloc(sizeof(*z));
if (!z)
return NULL;
z->src = NULL;
z->src_len = 0;
z->total = 0;
z->out = 0;
z->ext = 0;
z->done = 0;
z->stage = 0;
z->want = 0;
z->buf = NULL;
xxh64_reset(&z->xs, 0);
return z;
}
/* Room for a block: where the last left off, or the start of the ring
* again; and how much of the end of the ring the block's matches may
* reach back into, short of what the block and a wide copy past it can
* have written over */
static size_t rzstd_blocks_room(rzstd_blocks_t *z)
{
if (z->out + RZSTD_BLOCK_MAX > z->cap)
{
z->ext = z->out;
z->out = 0;
}
return z->ext > z->out + RZSTD_BLOCK_MAX + RZSTD_COPY_SLACK
? z->ext - (z->out + RZSTD_BLOCK_MAX + RZSTD_COPY_SLACK) : 0;
}
/* What a block's piece is: its body, then the next block's header or
* the frame's checksum */
static size_t rzstd_piece(const rzstd_frame_header_t *h,
const rzstd_block_header_t *b)
{
size_t body = b->type == RZSTD_BLOCK_RLE ? 1 : b->size;
return body + (b->last ? (h->has_checksum ? 4 : 0) : 3);
}
rzstd_blocks_t *rzstd_blocks_begin(const uint8_t *src, size_t src_len)
{
rzstd_blocks_t *z;
if (!src || !(z = rzstd_blocks_alloc()))
return NULL;
z->src = src;
z->src_len = src_len;
if ( rzstd_frame_begin(src, src_len, &z->h, &z->at, &z->st) != RZ_OK
|| !rzstd_blocks_buffer(z))
{
free(z);
return NULL;
}
return z;
}
int rzstd_blocks_step(rzstd_blocks_t *z, const uint8_t **data, size_t *len)
{
size_t from, ext;
int last = 0;
*data = NULL;
*len = 0;
if (!z || z->stage)
return RZSTD_PROCESS_ERROR;
if (z->done)
return RZSTD_PROCESS_END;
ext = rzstd_blocks_room(z);
from = z->out;
if (rzstd_frame_block(z->src, z->src_len, &z->at, z->buf, z->cap,
&z->out, &z->st, &last, z->buf + z->ext, ext) != RZ_OK)
return RZSTD_PROCESS_ERROR;
z->total += z->out - from;
if (z->h.has_checksum)
xxh64_update(&z->xs, z->buf + from, z->out - from);
*data = z->buf + from;
*len = z->out - from;
if (last)
{
/* the frame's end, and nothing after it */
if ( rzstd_frame_end(z->src, z->src_len, &z->h, &z->at, z->total,
&z->xs) != RZ_OK
|| z->at != z->src_len)
return RZSTD_PROCESS_ERROR;
z->done = 1;
}
return RZSTD_PROCESS_NEXT;
}
rzstd_blocks_t *rzstd_blocks_open(void)
{
rzstd_blocks_t *z = rzstd_blocks_alloc();
if (!z)
return NULL;
z->stage = 1;
z->want = 5;
return z;
}
size_t rzstd_blocks_want(const rzstd_blocks_t *z)
{
return z && !z->done ? z->want : 0;
}
int rzstd_blocks_give(rzstd_blocks_t *z, const uint8_t *src, size_t len,
const uint8_t **data, size_t *dlen)
{
*data = NULL;
*dlen = 0;
if (!z || !z->stage || z->done || !src || len != z->want)
return RZSTD_PROCESS_ERROR;
switch (z->stage)
{
case 1:
/* the magic and the descriptor, which say how long the rest of
* the header is */
memcpy(z->head, src, 5);
if ( rzstd_rd32(z->head) != RZSTD_MAGIC
|| rzstd_header_len(z->head) > RZSTD_FRAME_HEADER_MAX)
return RZSTD_PROCESS_ERROR;
z->want = rzstd_header_len(z->head) - 5 + 3;
z->stage = 2;
return RZSTD_PROCESS_NEXT;
case 2:
{
/* the rest of the header and the first block's */
size_t at = 0;
memcpy(z->head + 5, src, len);
if ( rzstd_frame_begin(z->head, 5 + len - 3, &z->h, &at,
&z->st) != RZ_OK
|| at != 5 + len - 3
|| !rzstd_blocks_buffer(z))
return RZSTD_PROCESS_ERROR;
rzstd_read_block_header(z->head + at, &z->next);
if (z->next.type == RZSTD_BLOCK_RESERVED
|| z->next.size > RZSTD_BLOCK_MAX)
return RZSTD_PROCESS_ERROR;
z->want = rzstd_piece(&z->h, &z->next);
z->stage = 3;
return RZSTD_PROCESS_NEXT;
}
default:
{
/* a block, decoded where it was read */
size_t used = 0;
size_t ext = rzstd_blocks_room(z);
size_t from = z->out;
if (rzstd_block_body(&z->next, src, len, &used, z->buf, z->cap,
&z->out, &z->st, z->buf + z->ext, ext) != RZ_OK)
return RZSTD_PROCESS_ERROR;
z->total += z->out - from;
if (z->h.has_checksum)
xxh64_update(&z->xs, z->buf + from, z->out - from);
*data = z->buf + from;
*dlen = z->out - from;
if (z->next.last)
{
size_t at = used;
if (rzstd_frame_end(src, len, &z->h, &at, z->total, &z->xs)
!= RZ_OK)
return RZSTD_PROCESS_ERROR;
z->done = 1;
z->want = 0;
return RZSTD_PROCESS_NEXT;
}
rzstd_read_block_header(src + used, &z->next);
if (z->next.type == RZSTD_BLOCK_RESERVED
|| z->next.size > RZSTD_BLOCK_MAX)
return RZSTD_PROCESS_ERROR;
z->want = rzstd_piece(&z->h, &z->next);
return RZSTD_PROCESS_NEXT;
}
}
}
void rzstd_blocks_free(rzstd_blocks_t *z)
{
if (!z)
return;
free(z->buf);
free(z);
}
/* -------- the public interface -------- */
int rzstd_decode(uint8_t *dst, size_t dst_len, const uint8_t *src,
@@ -4110,6 +4446,10 @@ int rzstd_encode(uint8_t *dst, size_t dst_len, const uint8_t *src,
* saves. */
{
size_t produced = 0;
/* The search moves the recent offsets as it goes; a block
* stored raw instead leaves the decoder's where they were,
* so they go back with it. */
uint32_t rep_was[3];
rzstd_seq_t *seq = (rzstd_seq_t*)malloc(
(take / RZSTD_ENC_MIN_MATCH + 1) * sizeof(rzstd_seq_t));
uint8_t *lits = (uint8_t*)malloc(take);
@@ -4117,6 +4457,9 @@ int rzstd_encode(uint8_t *dst, size_t dst_len, const uint8_t *src,
size_t lit_len = 0;
int ok = 0;
rep_was[0] = rep[0];
rep_was[1] = rep[1];
rep_was[2] = rep[2];
if (!hash)
{
/* Cleared once for the whole input, not once a block. */
@@ -4460,6 +4803,9 @@ int rzstd_encode(uint8_t *dst, size_t dst_len, const uint8_t *src,
in += take;
continue;
}
rep[0] = rep_was[0];
rep[1] = rep_was[1];
rep[2] = rep_was[2];
}
rzstd_write_block_header(dst + at, (uint32_t)take,
File diff suppressed because it is too large. Load diff
+233
View File
@@ -0,0 +1,233 @@
/* Copyright (C) 2010-2026 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (encoding_xxh64.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <string.h>
#if defined(_MSC_VER)
#include <stdlib.h>
#endif
#include <retro_inline.h>
#include <retro_endianness.h>
#include <encodings/xxh64.h>
#define XXH64_P1 0x9E3779B185EBCA87ULL
#define XXH64_P2 0xC2B2AE3D27D4EB4FULL
#define XXH64_P3 0x165667B19E3779F9ULL
#define XXH64_P4 0x85EBCA77C2B2AE63ULL
#define XXH64_P5 0x27D4EB2F165667C5ULL
#if defined(_MSC_VER)
#define XXH64_ROTL(x, r) _rotl64((x), (r))
#else
#define XXH64_ROTL(x, r) (((x) << (r)) | ((x) >> (64 - (r))))
#endif
/* One lane's step: the octets in, multiplied, turned and multiplied */
#define XXH64_ROUND(acc, in) \
((acc) += (in) * XXH64_P2, (acc) = XXH64_ROTL((acc), 31), \
(acc) *= XXH64_P1)
static INLINE uint64_t xxh64_rd64(const uint8_t *p)
{
uint64_t v;
memcpy(&v, p, 8);
return swap_if_big64(v);
}
static INLINE uint32_t xxh64_rd32(const uint8_t *p)
{
uint32_t v;
memcpy(&v, p, 4);
return swap_if_big32(v);
}
/* The whole stripes of @p up to @end, at least one, through the lanes
* @v, which are held in registers for it; returns where the stripes
* stop.
*
* The lanes are chains of multiplies, bound by the multiplier however
* the loop is unrolled, so it takes a stripe a turn; what it saves is
* the branch a varying length mispredicts: one stripe is taken without
* the loop. */
static INLINE const uint8_t *xxh64_stripes(uint64_t *v, const uint8_t *p,
const uint8_t *end)
{
uint64_t v1 = v[0];
uint64_t v2 = v[1];
uint64_t v3 = v[2];
uint64_t v4 = v[3];
size_t n = (size_t)(end - p);
const uint8_t *stop = p + (n & ~(size_t)31);
if (n < 64)
{
XXH64_ROUND(v1, xxh64_rd64(p));
XXH64_ROUND(v2, xxh64_rd64(p + 8));
XXH64_ROUND(v3, xxh64_rd64(p + 16));
XXH64_ROUND(v4, xxh64_rd64(p + 24));
p += 32;
}
else
do
{
XXH64_ROUND(v1, xxh64_rd64(p));
XXH64_ROUND(v2, xxh64_rd64(p + 8));
XXH64_ROUND(v3, xxh64_rd64(p + 16));
XXH64_ROUND(v4, xxh64_rd64(p + 24));
p += 32;
} while (p != stop);
v[0] = v1;
v[1] = v2;
v[2] = v3;
v[3] = v4;
return p;
}
static INLINE uint64_t xxh64_merge(uint64_t h, uint64_t v)
{
uint64_t k = 0;
XXH64_ROUND(k, v);
h ^= k;
return h * XXH64_P1 + XXH64_P4;
}
/* The four lanes folded into one */
static INLINE uint64_t xxh64_converge(const uint64_t *v)
{
uint64_t h = XXH64_ROTL(v[0], 1) + XXH64_ROTL(v[1], 7)
+ XXH64_ROTL(v[2], 12) + XXH64_ROTL(v[3], 18);
h = xxh64_merge(h, v[0]);
h = xxh64_merge(h, v[1]);
h = xxh64_merge(h, v[2]);
return xxh64_merge(h, v[3]);
}
#define XXH64_TAKE8(h, p) do { \
uint64_t k_ = 0; \
XXH64_ROUND(k_, xxh64_rd64(p)); \
(h) ^= k_; \
(h) = XXH64_ROTL((h), 27) * XXH64_P1 + XXH64_P4; \
} while (0)
#define XXH64_TAKE1(h, c) do { \
(h) ^= (uint64_t)(c) * XXH64_P5; \
(h) = XXH64_ROTL((h), 11) * XXH64_P1; \
} while (0)
/* The last @len (under 32) octets at @p, and the avalanche: eights,
* then a four, then single octets */
static INLINE uint64_t xxh64_finish(uint64_t h, const uint8_t *p, size_t len)
{
for (; len >= 8; len -= 8, p += 8)
XXH64_TAKE8(h, p);
if (len >= 4)
{
h ^= (uint64_t)xxh64_rd32(p) * XXH64_P1;
h = XXH64_ROTL(h, 23) * XXH64_P2 + XXH64_P3;
p += 4;
len -= 4;
}
for (; len; len--)
XXH64_TAKE1(h, *p++);
h ^= h >> 33;
h *= XXH64_P2;
h ^= h >> 29;
h *= XXH64_P3;
h ^= h >> 32;
return h;
}
static void xxh64_lanes(uint64_t *v, uint64_t seed)
{
v[0] = seed + XXH64_P1 + XXH64_P2;
v[1] = seed + XXH64_P2;
v[2] = seed;
v[3] = seed - XXH64_P1;
}
uint64_t xxh64(const void *data, size_t len, uint64_t seed)
{
const uint8_t *p = (const uint8_t*)data;
uint64_t h;
if (len >= 32)
{
uint64_t v[4];
xxh64_lanes(v, seed);
p = xxh64_stripes(v, p, p + len);
h = xxh64_converge(v);
}
else
h = seed + XXH64_P5;
return xxh64_finish(h + (uint64_t)len, p, len & 31);
}
void xxh64_reset(xxh64_state_t *s, uint64_t seed)
{
xxh64_lanes(s->v, seed);
s->total = 0;
s->memsize = 0;
}
void xxh64_update(xxh64_state_t *s, const void *data, size_t len)
{
const uint8_t *p = (const uint8_t*)data;
const uint8_t *end = p + len;
if (!len)
return;
s->total += len;
/* not yet a stripe */
if (s->memsize + len < 32)
{
memcpy(s->mem + s->memsize, p, len);
s->memsize += (uint32_t)len;
return;
}
/* the stripe begun before, made whole */
if (s->memsize)
{
size_t fill = 32 - s->memsize;
memcpy(s->mem + s->memsize, p, fill);
xxh64_stripes(s->v, s->mem, s->mem + 32);
p += fill;
s->memsize = 0;
}
if (end - p >= 32)
p = xxh64_stripes(s->v, p, end);
if (p != end)
{
memcpy(s->mem, p, (size_t)(end - p));
s->memsize = (uint32_t)(end - p);
}
}
uint64_t xxh64_digest(const xxh64_state_t *s)
{
uint64_t h = s->total >= 32
? xxh64_converge(s->v)
: s->v[2] + XXH64_P5;
return xxh64_finish(h + s->total, s->mem, s->memsize);
}
+9 -3
View File
@@ -137,12 +137,17 @@ static size_t cpu_class_by_value(const char *leaf, unsigned pct,
unsigned long best = 0;
size_t i, n = 0;
/* 32-bit is plenty: kHz clocks top out below 10^7 and capacities
* at 1024; unsigned long would double the frame past the 2 KiB
* thread-stack budget (PSP, GX). */
unsigned vals[CPU_CLASS_MAX_IDS];
* at 1024. On the heap, so the callers' frames stay within the
* 2 KiB thread-stack budget (PSP, GX). */
unsigned *vals;
if (len > CPU_CLASS_MAX_IDS)
len = CPU_CLASS_MAX_IDS;
if (!(vals = (unsigned*)malloc(len * sizeof(*vals) + 1)))
{
memset(cls, 0, len);
return 0;
}
for (i = 0; i < len; i++)
{
char path[256];
@@ -158,6 +163,7 @@ static size_t cpu_class_by_value(const char *leaf, unsigned pct,
}
for (i = 0; i < len; i++)
cls[i] = (vals[i] && vals[i] * 100 >= best * pct) ? 1 : 0;
free(vals);
return n;
}
+46 -10
View File
@@ -1290,8 +1290,31 @@ static uint64_t cpu_features_probe(void)
cpu |= RETRO_SIMD_AVX2;
_val = 0;
_len = sizeof(_val);
if (sysctlbyname("hw.optional.avx512f", &_val, &_len, NULL, 0) == 0 && _val)
cpu |= RETRO_SIMD_AVX512;
/* RETRO_SIMD_AVX512 means the x86-64-v4 baseline (F, CD, BW, DQ,
* VL), so every one of those keys has to answer. Darwin enables ZMM
* state lazily on first use, which is why these sysctls and not
* xgetbv decide it here. */
{
static const char *avx512_keys[] = {
"hw.optional.avx512f", "hw.optional.avx512cd",
"hw.optional.avx512bw", "hw.optional.avx512dq",
"hw.optional.avx512vl"
};
size_t k;
int have_avx512 = 1;
for (k = 0; k < sizeof(avx512_keys) / sizeof(avx512_keys[0]); k++)
{
_val = 0;
_len = sizeof(_val);
if (!(sysctlbyname(avx512_keys[k], &_val, &_len, NULL, 0) == 0 && _val))
{
have_avx512 = 0;
break;
}
}
if (have_avx512)
cpu |= RETRO_SIMD_AVX512;
}
_val = 0;
_len = sizeof(_val);
if (sysctlbyname("hw.optional.altivec", &_val, &_len, NULL, 0) == 0 && _val)
@@ -1475,20 +1498,33 @@ static uint64_t cpu_features_probe(void)
memset(flags7, 0, sizeof(flags7));
#endif
if (flags7[1] & (1 << 5))
/* AVX2 executes on YMM registers, so like AVX it needs the OS
* to save that state, not just the CPUID bit. */
if (ymm_state && (flags7[1] & (1 << 5)))
cpu |= RETRO_SIMD_AVX2;
/* SHA-NI is one bit covering both digests. */
if (flags7[1] & (1 << 29))
cpu |= RETRO_SIMD_SHA1 | RETRO_SIMD_SHA256;
/* AVX-512 Foundation detection.
* Requires CPUID leaf 7 sub-leaf 0 EBX bit 16 (AVX-512F),
* and OS support for saving ZMM state:
* xgetbv XCR0 bits 1,2 (SSE/AVX) and 5,6,7 (opmask, ZMM_Hi256, Hi16_ZMM). */
if ((flags7[1] & (1 << 16))
&& ((xgetbv_x86(0) & 0xe6) == 0xe6))
cpu |= RETRO_SIMD_AVX512;
/* AVX-512 means the x86-64-v4 baseline: leaf 7 sub-leaf 0 EBX
* bits 16 (F), 17 (DQ), 28 (CD), 30 (BW) and 31 (VL). Code that
* checks this one flag may then use any of them; that is every
* AVX-512 CPU except the Xeon Phi parts, which lack BW/DQ/VL.
*
* The OS must also save opmask and ZMM state (XCR0 bits 5,6,7 on
* top of 1,2). xgetbv is only valid once OSXSAVE is set, and
* ymm_state already proved that, so test it first: without it
* xgetbv faults on a CPU that has AVX-512 but an OS that never
* enabled XSAVE. */
{
const uint32_t avx512_v4 = (1u << 16) | (1u << 17)
| (1u << 28) | (1u << 30) | (1u << 31);
if ( ymm_state
&& (((uint32_t)flags7[1] & avx512_v4) == avx512_v4)
&& ((xgetbv_x86(0) & 0xe6) == 0xe6))
cpu |= RETRO_SIMD_AVX512;
}
/* Leaf 7 EAX carries the highest sub-leaf the CPU implements, so
* consult it before asking for sub-leaf 1: an out of range
+53 -25
View File
@@ -39,8 +39,8 @@ static int file_archive_get_file_list_cb(
const char *valid_exts,
const uint8_t *cdata,
unsigned cmode,
uint32_t csize,
uint32_t size,
uint64_t csize,
uint64_t size,
uint32_t checksum,
struct archive_extract_userdata *userdata)
{
@@ -93,7 +93,7 @@ static int file_archive_get_file_list_cb(
static int file_archive_extract_cb(const char *name, const char *valid_exts,
const uint8_t *cdata,
unsigned cmode, uint32_t csize, uint32_t size,
unsigned cmode, uint64_t csize, uint64_t size,
uint32_t checksum, struct archive_extract_userdata *userdata)
{
const char *ext = path_get_extension(name);
@@ -143,6 +143,39 @@ static int file_archive_extract_cb(const char *name, const char *valid_exts,
/* The largest archive a 32-bit host maps whole. */
#define ARCHIVE_MAP_CEILING_32 (256 * 1024 * 1024)
/* Ask the VFS to map the archive. A 64-bit host maps it whatever its
* size, since pages are only faulted in as they are touched; a 32-bit
* one keeps the mapping to ARCHIVE_MAP_CEILING_32 so a huge archive
* cannot take the address space the core needs, and reads it through
* the stream instead. A URL scheme or a failed map just leaves no
* mapping, which every backend reads through the stream as well. */
unsigned file_archive_open_hints(int64_t size)
{
return (size > 0 && (sizeof(size_t) >= 8
|| size <= ARCHIVE_MAP_CEILING_32))
? RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS
: RETRO_VFS_FILE_ACCESS_HINT_NONE;
}
const uint8_t *file_archive_whole(RFILE *file, int64_t size, void **owned)
{
const uint8_t *src = NULL;
*owned = NULL;
filestream_seek(file, 0, RETRO_VFS_SEEK_POSITION_START);
if (filestream_view(file, size, &src) == size)
return src;
if (!(*owned = malloc((size_t)size)))
return NULL;
filestream_seek(file, 0, RETRO_VFS_SEEK_POSITION_START);
if (filestream_read(file, *owned, size) != size)
{
free(*owned);
*owned = NULL;
return NULL;
}
return (const uint8_t*)*owned;
}
static int file_archive_parse_file_init(file_archive_transfer_t *state,
const char *file)
{
@@ -157,25 +190,11 @@ static int file_archive_parse_file_init(file_archive_transfer_t *state,
if (!(state->backend = file_archive_get_file_backend(path)))
return -1;
/* Ask the VFS to map the archive. A 64-bit host maps it whatever
* its size, since pages are only faulted in as they are touched;
* a 32-bit one keeps the mapping to ARCHIVE_MAP_CEILING_32 so a
* huge archive cannot take the address space the core needs, and
* reads it through the stream instead. A URL scheme or a failed
* map just leaves no mapping, which every backend reads through
* the stream as well. */
{
int64_t sz = path_get_size(path);
unsigned hints = (sz > 0 && (sizeof(size_t) >= 8
|| sz <= ARCHIVE_MAP_CEILING_32))
? RETRO_VFS_FILE_ACCESS_HINT_FREQUENT_ACCESS
: RETRO_VFS_FILE_ACCESS_HINT_NONE;
/* Failed to open archive. */
if (!(state->archive_file = filestream_open(path,
RETRO_VFS_FILE_ACCESS_READ, hints)))
return -1;
}
/* Failed to open archive. */
if (!(state->archive_file = filestream_open(path,
RETRO_VFS_FILE_ACCESS_READ,
file_archive_open_hints(path_get_size(path)))))
return -1;
state->archive_size = filestream_get_size(state->archive_file);
@@ -463,6 +482,7 @@ bool file_archive_extract_file(
userdata.list = NULL;
userdata.found_file = false;
userdata.list_only = false;
userdata.crc_wanted = false;
userdata.crc = 0;
userdata.transfer = NULL;
userdata.dec = NULL;
@@ -517,6 +537,7 @@ bool file_archive_get_file_list_noalloc(struct string_list *list,
userdata.list = list;
userdata.found_file = false;
userdata.list_only = true;
userdata.crc_wanted = false;
userdata.crc = 0;
userdata.transfer = NULL;
userdata.dec = NULL;
@@ -551,6 +572,7 @@ struct string_list *file_archive_get_file_list(const char *path,
userdata.list = string_list_new();
userdata.found_file = false;
userdata.list_only = true;
userdata.crc_wanted = false;
userdata.crc = 0;
userdata.transfer = NULL;
userdata.dec = NULL;
@@ -713,7 +735,7 @@ int file_archive_perform_mode_step(file_archive_transfer_t *state)
* Returns 1 if it finished immediately, 0 if it parked and wants to be
* resumed through file_archive_perform_mode_step(), -1 on failure. */
int file_archive_perform_mode_start(const char *path, const char *valid_exts,
const uint8_t *cdata, unsigned cmode, uint32_t csize, uint32_t size,
const uint8_t *cdata, unsigned cmode, uint64_t csize, uint64_t size,
uint32_t crc32, struct archive_extract_userdata *userdata)
{
file_archive_transfer_t *state;
@@ -751,7 +773,7 @@ int file_archive_perform_mode_start(const char *path, const char *valid_exts,
}
bool file_archive_perform_mode(const char *path, const char *valid_exts,
const uint8_t *cdata, unsigned cmode, uint32_t csize, uint32_t size,
const uint8_t *cdata, unsigned cmode, uint64_t csize, uint64_t size,
uint32_t crc32, struct archive_extract_userdata *userdata)
{
/* The blocking form, for callers that need the whole member before
@@ -924,7 +946,7 @@ const struct file_archive_file_backend *file_archive_get_rar_file_backend(void)
const struct file_archive_file_backend* file_archive_get_file_backend(const char *path)
{
#if defined(HAVE_7ZIP) || defined(HAVE_ZLIB) || defined(HAVE_RZSTD) || defined(HAVE_RRAR) || defined(HAVE_COMPRESSION)
#if defined(HAVE_7ZIP) || defined(HAVE_ZLIB) || defined(HAVE_RZSTD) || defined(HAVE_RRAR) || defined(HAVE_RBZIP2) || defined(HAVE_COMPRESSION)
char newpath[PATH_MAX_LENGTH];
const char *file_ext = NULL;
char *last = NULL;
@@ -955,6 +977,11 @@ const struct file_archive_file_backend* file_archive_get_file_backend(const char
return &zstd_backend;
#endif
#ifdef HAVE_RBZIP2
if (string_is_equal_noncase(file_ext, "bz2"))
return &bz2_backend;
#endif
#ifdef HAVE_RRAR
if (string_is_equal_noncase(file_ext, "rar"))
return &rar_backend;
@@ -1010,6 +1037,7 @@ uint32_t file_archive_get_file_crc32_and_size(const char *path, uint64_t *size)
memset(&state, 0, sizeof(state));
state.type = ARCHIVE_TRANSFER_INIT;
userdata.crc_wanted = true;
/* Initialize and open archive first.
Sets next state type to ITERATE. */
+209 -61
View File
@@ -51,13 +51,74 @@
struct sevenzip_context_t
{
r7z_archive_t *archive;
/* Cache of the last extracted member, owned here so the handle
* handed to the caller stays valid until the next call. */
uint8_t *output;
file_archive_transfer_t *state;
uint8_t *member; /* a member gathered out of a stream */
size_t gathered;
uint32_t parse_index;
uint32_t decompress_index;
bool streaming; /* the member goes to the sink in pieces */
bool gathering; /* or into member */
};
/* A member bound for a file, bigger than this, is streamed to it */
#define SEVENZIP_STREAM_OVER (256 * 1024)
/* A folder of other members, bigger than this, is never held whole: a
* member wanted out of it is streamed, and the next one in the folder
* goes on from there */
#ifndef SEVENZIP_FOLDER_KEEP
#define SEVENZIP_FOLDER_KEEP (32 * 1024 * 1024)
#endif
static bool sevenzip_stream_whole(r7z_archive_t *a, uint32_t index)
{
const r7z_entry_t *e = r7z_archive_entry(a, index);
uint64_t folder;
if (!e || e->is_dir || !e->size)
return false;
folder = r7z_archive_folder_size(a, e->folder);
return folder > SEVENZIP_FOLDER_KEEP
&& !(e->offset_in_folder == 0 && e->size == folder);
}
/* Member @index streamed into *@out (a NUL after it), in one go; false
* where its folder cannot be streamed or it does not come out */
static bool sevenzip_stream_into(r7z_archive_t *a, uint32_t index,
uint8_t **out, size_t *len)
{
const r7z_entry_t *e = r7z_archive_entry(a, index);
const uint8_t *d;
size_t n, at = 0;
uint8_t *m;
int res;
if ( !e || e->size >= (uint64_t)((size_t)-1)
|| r7z_archive_stream_begin(a, index) != R7Z_OK)
return false;
if (!(m = (uint8_t*)malloc((size_t)e->size + 1)))
{
r7z_archive_stream_end(a);
return false;
}
while ((res = r7z_archive_stream_step(a, &d, &n)) == R7Z_PENDING)
{
if (n > e->size - at)
break;
if (n)
memcpy(m + at, d, n);
at += n;
}
if (res != R7Z_OK || at != e->size)
{
r7z_archive_stream_end(a);
free(m);
return false;
}
m[at] = '\0';
*out = m;
*len = at;
return true;
}
static int sevenzip_open_archive(struct sevenzip_context_t *ctx,
file_archive_transfer_t *state)
{
@@ -87,10 +148,10 @@ static void sevenzip_parse_file_free(void *context)
if (!sevenzip_context)
return;
free(sevenzip_context->output);
/* The mapping and the stream belong to archive_file.c, which
* releases them after this. */
r7z_archive_close(sevenzip_context->archive);
free(sevenzip_context->member);
free(sevenzip_context);
}
@@ -104,13 +165,36 @@ typedef struct
bool found;
} sevenzip_decomp_state_t;
/* One slice of decoding member @index's folder: R7Z_PENDING while
* there is more, and on R7Z_OK the member where it is held in the
* archive's folder cache - decoded there, or, for a stored folder of a
* mapped archive, the archive itself - valid until another folder is
* decoded. Nothing is copied. */
static int sevenzip_member_step(r7z_archive_t *a, uint32_t index,
const uint8_t **data, size_t *len)
{
const r7z_entry_t *e = r7z_archive_entry(a, index);
int res;
if (!e || e->is_dir)
return R7Z_ERROR_PARAM;
if (!e->size)
{
*data = (const uint8_t*)"";
*len = 0;
return R7Z_OK;
}
if ((res = r7z_archive_decode_step(a, index)) != R7Z_OK)
return res;
return r7z_archive_entry_borrow(a, index, data, len);
}
/* Called once per entry while the driver walks the archive. Starts the
* decode of the wanted member and stops the scan; the iterate loop
* finishes the decode across later calls. */
static int sevenzip_file_decompressed(
const char *name, const char *valid_exts,
const uint8_t *cdata, unsigned cmode,
uint32_t csize, uint32_t size,
uint64_t csize, uint64_t size,
uint32_t crc32, struct archive_extract_userdata *userdata)
{
sevenzip_decomp_state_t *st =
@@ -137,35 +221,45 @@ static int sevenzip_file_decompressed(
{
struct sevenzip_context_t *ctx =
(struct sevenzip_context_t*)userdata->transfer->context;
uint8_t *member = NULL;
size_t member_len = 0;
int res;
const r7z_entry_t *e;
uint8_t *member = NULL;
size_t member_len = 0;
uint32_t index = (uint32_t)(size_t)cdata;
int res = R7Z_OK;
if (!ctx || !ctx->archive)
if ( !ctx || !ctx->archive
|| !(e = r7z_archive_entry(ctx->archive, index)) || e->is_dir)
return 1;
/* out of a big folder of others, streamed to it */
if ( sevenzip_stream_whole(ctx->archive, index)
&& sevenzip_stream_into(ctx->archive, index, &member,
&member_len))
{
*st->buf = member;
st->size = member_len;
st->found = true;
return 0;
}
/* Sliced, but driven to completion here: the caller of
* compressed_file_read wants the bytes on return and has nowhere
* to yield to. */
do
{
res = r7z_archive_extract_slice(ctx->archive,
(uint32_t)(size_t)cdata, &member, &member_len);
} while (res == R7Z_PENDING);
if (e->size)
while ((res = r7z_archive_decode_step(ctx->archive, index))
== R7Z_PENDING) { }
if (res != R7Z_OK)
/* the caller's to keep, with a NUL after the data: a member that
* is its folder is that folder's buffer, handed over, and any
* other is copied out of it */
if ( res != R7Z_OK
|| r7z_archive_extract_detach(ctx->archive, index, &member,
&member_len) != R7Z_OK)
return 1;
/* RetroArch expects a NUL after the data. */
if ((*st->buf = malloc(member_len + 1)))
{
((char*)(*st->buf))[member_len] = '\0';
if (member_len)
memcpy(*st->buf, member, member_len);
st->size = member_len;
st->found = true;
}
free(member);
member[member_len] = '\0';
*st->buf = member;
st->size = member_len;
st->found = true;
}
return 0;
@@ -241,7 +335,7 @@ static int64_t sevenzip_file_read(
static bool sevenzip_stream_decompress_data_to_file_init(
void *context, file_archive_file_handle_t *handle,
const uint8_t *cdata, unsigned cmode, uint32_t csize, uint32_t size)
const uint8_t *cdata, unsigned cmode, uint64_t csize, uint64_t size)
{
struct sevenzip_context_t *sevenzip_context =
(struct sevenzip_context_t*)context;
@@ -249,13 +343,44 @@ static bool sevenzip_stream_decompress_data_to_file_init(
(void)handle;
(void)cmode;
(void)csize;
(void)size;
if (!sevenzip_context)
return false;
/* The parse step hands the entry index through cdata. */
sevenzip_context->decompress_index = (uint32_t)(size_t)cdata;
sevenzip_context->streaming = false;
sevenzip_context->gathering = false;
free(sevenzip_context->member);
sevenzip_context->member = NULL;
sevenzip_context->gathered = 0;
/* A big member bound for a file goes to it through a window, and
* one out of a big folder of others into a buffer of its own, where
* its folder's coders allow; anything else is decoded whole */
{
bool sink = sevenzip_context->state->pending_sink
&& size > SEVENZIP_STREAM_OVER;
bool apart = !sink && sevenzip_stream_whole(sevenzip_context->archive,
sevenzip_context->decompress_index);
if (sink || apart)
{
int res = r7z_archive_stream_begin(sevenzip_context->archive,
sevenzip_context->decompress_index);
if (res == R7Z_OK)
{
if ( apart
&& (size >= (uint64_t)((size_t)-1)
|| !(sevenzip_context->member = (uint8_t*)malloc(
(size_t)size + 1))))
return false;
sevenzip_context->streaming = sink;
sevenzip_context->gathering = apart;
}
else if (res != R7Z_ERROR_UNSUPPORTED)
return false;
}
}
return true;
}
@@ -265,9 +390,9 @@ static int sevenzip_stream_decompress_data_to_file_iterate(
{
struct sevenzip_context_t *sevenzip_context =
(struct sevenzip_context_t*)context;
uint8_t *member = NULL;
size_t member_len = 0;
int res;
const uint8_t *member = NULL;
size_t member_len = 0;
int res;
/* The callers loop while this returns 0, so 0 means "not finished,
* call again" and never "failed". Returning it on error spins them
@@ -276,26 +401,63 @@ static int sevenzip_stream_decompress_data_to_file_iterate(
if (!sevenzip_context || !sevenzip_context->archive)
return -1;
if (sevenzip_context->gathering)
{
const r7z_entry_t *e = r7z_archive_entry(sevenzip_context->archive,
sevenzip_context->decompress_index);
res = r7z_archive_stream_step(sevenzip_context->archive,
&member, &member_len);
if (res == R7Z_PENDING)
{
if (member_len > e->size - sevenzip_context->gathered)
return -1;
if (member_len)
memcpy(sevenzip_context->member + sevenzip_context->gathered,
member, member_len);
sevenzip_context->gathered += member_len;
return 0;
}
sevenzip_context->gathering = false;
if (res != R7Z_OK || sevenzip_context->gathered != e->size)
return -1;
if (handle)
handle->data = sevenzip_context->member;
return 1;
}
if (sevenzip_context->streaming)
{
res = r7z_archive_stream_step(sevenzip_context->archive,
&member, &member_len);
if (res == R7Z_PENDING)
return (!member_len || filestream_write(
sevenzip_context->state->pending_sink, member,
(int64_t)member_len) == (int64_t)member_len) ? 0 : -1;
sevenzip_context->streaming = false;
if (res != R7Z_OK)
return -1;
/* in the sink, whole */
if (handle)
handle->data = NULL;
return 1;
}
/* Sliced rather than whole: a solid folder is tens of milliseconds
* decoded in one go, and this is the return value the archive layer
* turns into a yield. Callers that cannot yield still get the whole
* member, they just get it through several calls. */
res = r7z_archive_extract_slice(sevenzip_context->archive,
sevenzip_context->decompress_index,
&member, &member_len);
res = sevenzip_member_step(sevenzip_context->archive,
sevenzip_context->decompress_index, &member, &member_len);
if (res == R7Z_PENDING)
return 0;
if (res != R7Z_OK)
return -1;
/* The caller reads through handle->data until it calls back in, so
* the previous member cannot be released any earlier than this. */
free(sevenzip_context->output);
sevenzip_context->output = member;
/* The caller only reads the member, and is done with it before it
* calls back in for another: lent where it is held */
if (handle)
handle->data = sevenzip_context->output;
handle->data = (uint8_t*)member;
return 1;
}
@@ -324,6 +486,7 @@ static int sevenzip_parse_file_init(file_archive_transfer_t *state,
goto error;
state->context = sevenzip_context;
sevenzip_context->state = state;
if (!sevenzip_open_archive(sevenzip_context, state))
goto error;
@@ -344,8 +507,8 @@ static int sevenzip_parse_file_iterate_step_internal(
char *s,
const uint8_t **cdata,
unsigned *cmode,
uint32_t *size,
uint32_t *csize,
uint64_t *size,
uint64_t *csize,
uint32_t *checksum,
unsigned *payback,
struct archive_extract_userdata *userdata)
@@ -370,28 +533,13 @@ static int sevenzip_parse_file_iterate_step_internal(
if (!sevenzip_name_to_char(entry->name, s, PATH_MAX_LENGTH))
return -1;
/* The vtable reports sizes as uint32_t, but 7z stores them as
* 64-bit. A member of 4 GiB or more cannot be described here, and
* truncating would hand the caller a wrapped length to write from
* a full-length buffer. Skip such an entry instead: the scan
* carries on and everything else in the archive stays usable. */
if (entry->size > (uint64_t)0xFFFFFFFFu)
{
/* Blank the name too: the caller passes whatever is in it to
* file_cb, and a named entry reporting zero bytes is worse
* than no entry at all. */
s[0] = '\0';
*payback = 1;
return 1;
}
*cmode = 0; /* unused for 7zip */
*checksum = entry->crc;
*size = (uint32_t)entry->size;
*size = entry->size;
/* The packed size of an individual member is not meaningful for
* 7z: members share a solid folder's compressed stream. Callers
* use this only for progress reporting. */
*csize = (uint32_t)entry->size;
*csize = entry->size;
*cdata = (uint8_t *)(size_t)sevenzip_context->parse_index;
}
@@ -408,8 +556,8 @@ static int sevenzip_parse_file_iterate_step(void *context,
{
const uint8_t *cdata = NULL;
uint32_t checksum = 0;
uint32_t size = 0;
uint32_t csize = 0;
uint64_t size = 0;
uint64_t csize = 0;
unsigned cmode = 0;
unsigned payload = 0;
struct sevenzip_context_t *sevenzip_context =
+437
View File
@@ -0,0 +1,437 @@
/* Copyright (C) 2010-2026 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (archive_file_bz2.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* .bz2 files: one bzip2 stream, standing for the file named as the
* .bz2 is without its extension. A stream does not say how long it
* decodes to, so it is decoded when it is listed, a block at a time,
* for its size and CRC; a member that comes to no more than
* ARCHIVE_BZ2_KEEP octets is kept for the extraction that usually
* follows, a bigger one is decoded again when it is extracted, to its
* file a block at a time. The stream is decoded where the file stream
* lends it whole, or else read through a ring that holds at least the
* most a block can take ahead of the decoder. */
#include <stdlib.h>
#include <string.h>
#include <boolean.h>
#include <file/archive_file.h>
#include <file/file_path.h>
#include <streams/file_stream.h>
#include <encodings/crc32.h>
#include <encodings/rbzip2.h>
#include <string/stdstring.h>
#include <compat/strl.h>
#ifndef ARCHIVE_BZ2_KEEP
#define ARCHIVE_BZ2_KEEP (8 * 1024 * 1024)
#endif
/* How far the stream is read ahead of the decoder, in a ring of twice
* it */
#ifndef ARCHIVE_BZ2_AHEAD
#define ARCHIVE_BZ2_AHEAD RBZIP2_AHEAD
#endif
/* The stream, a block at a time */
struct bz2_src
{
rbzip2_t *bz;
RFILE *file;
uint8_t *ring; /* where the stream is not lent whole */
uint64_t at; /* read into the ring up to here */
uint64_t size;
size_t cap; /* the ring's */
};
struct bz2_file_context
{
file_archive_transfer_t *state;
struct bz2_src src;
uint8_t *data; /* the member, kept or decoded whole */
size_t size;
char inner_filename[PATH_MAX_LENGTH];
bool parsed;
bool streaming; /* extracting to the sink a block a call */
};
/* The member's name: the .bz2's without its extension */
static void bz2_inner_filename(const char *path, char *s, size_t len)
{
char *ext;
strlcpy(s, path_basename(path), len);
if ((ext = strrchr(s, '.')) && string_is_equal_noncase(ext, ".bz2"))
*ext = '\0';
}
/* The ring read on, up to a whole ring past @taken or the end */
static bool bz2_fill(struct bz2_src *s, uint64_t taken)
{
uint64_t to = taken + s->cap;
if (to > s->size)
to = s->size;
filestream_seek(s->file, (int64_t)s->at, RETRO_VFS_SEEK_POSITION_START);
while (s->at < to)
{
size_t at = (size_t)(s->at % s->cap);
size_t n = s->cap - at;
if (n > to - s->at)
n = (size_t)(to - s->at);
if (filestream_read(s->file, s->ring + at, (int64_t)n) != (int64_t)n)
return false;
s->at += n;
}
return true;
}
static bool bz2_src_begin(struct bz2_src *s, RFILE *file, int64_t size)
{
const uint8_t *src = NULL;
size_t cap = 2 * (size_t)ARCHIVE_BZ2_AHEAD;
if (size < 4 || (uint64_t)size > (uint64_t)((size_t)-1))
return false;
if (!s->bz && !(s->bz = rbzip2_new()))
return false;
s->file = file;
s->size = (uint64_t)size;
s->at = 0;
filestream_seek(file, 0, RETRO_VFS_SEEK_POSITION_START);
if (filestream_view(file, size, &src) == size)
{
free(s->ring);
s->ring = NULL;
return rbzip2_begin_blocks(s->bz, src, (size_t)size) == RBZIP2_OK;
}
if ((uint64_t)size < (uint64_t)cap)
cap = (size_t)size;
if (!s->ring || s->cap < cap)
{
free(s->ring);
if (!(s->ring = (uint8_t*)malloc(cap)))
return false;
}
s->cap = cap;
return bz2_fill(s, 0)
&& rbzip2_begin_ring(s->bz, s->ring, (size_t)s->at, NULL, 0)
== RBZIP2_OK;
}
/* The next block: RBZIP2_PENDING with it, RBZIP2_OK at the stream's
* end, or an error */
static int bz2_src_next(struct bz2_src *s, const uint8_t **blk, size_t *len)
{
if ( s->ring
&& s->at < s->size
&& s->at - rbzip2_taken(s->bz) < ARCHIVE_BZ2_AHEAD)
{
/* the ring filled again up to what the decoder still needs, and
* that handed over again, in two where it wraps */
uint64_t t = rbzip2_taken(s->bz);
size_t from = (size_t)(t % s->cap);
size_t have;
*blk = NULL;
*len = 0;
if (!bz2_fill(s, t))
return RBZIP2_ERROR_DATA;
have = (size_t)(s->at - t);
if (have > s->cap - from)
rbzip2_input(s->bz, s->ring + from, s->cap - from,
s->ring, have - (s->cap - from));
else
rbzip2_input(s->bz, s->ring + from, have, NULL, 0);
}
return rbzip2_step_block(s->bz, blk, len);
}
static void bz2_src_free(struct bz2_src *s)
{
rbzip2_free(s->bz);
free(s->ring);
s->bz = NULL;
s->ring = NULL;
}
/* What every block is handed to by bz2_pass() */
typedef bool (*bz2_sink_t)(void *ud, const uint8_t *blk, size_t len);
/* The whole stream of @file through @sink; false if it is not one */
static bool bz2_pass(struct bz2_src *s, RFILE *file, int64_t size,
bz2_sink_t sink, void *ud)
{
const uint8_t *blk;
size_t len;
int e;
if (!bz2_src_begin(s, file, size))
return false;
while ((e = bz2_src_next(s, &blk, &len)) == RBZIP2_PENDING)
if (!sink(ud, blk, len))
return false;
return e == RBZIP2_OK;
}
/* A member gathered whole: grown as it comes when its size is not
* known, or put where it goes when it is; @max past which it is not
* kept, which is not a failure */
struct bz2_whole
{
uint8_t *data;
size_t len;
size_t cap;
size_t max;
uint32_t crc;
uint64_t total;
bool dropped;
};
static bool bz2_gather(void *ud, const uint8_t *blk, size_t len)
{
struct bz2_whole *w = (struct bz2_whole*)ud;
w->crc = encoding_crc32(w->crc, blk, len);
w->total += len;
if (w->dropped)
return true;
if (len > w->max - w->len)
{
free(w->data);
w->data = NULL;
w->dropped = true;
return true;
}
if (len > w->cap - w->len)
{
/* the octet to spare after it, for a NUL */
size_t cap = w->cap ? w->cap + 1 : 65536;
uint8_t *d;
while (cap - 1 - w->len < len)
{
if (cap > ((size_t)-1) / 2)
return false;
cap *= 2;
}
if (!(d = (uint8_t*)realloc(w->data, cap)))
return false;
w->data = d;
w->cap = cap - 1;
}
memcpy(w->data + w->len, blk, len);
w->len += len;
return true;
}
static bool bz2_write(void *ud, const uint8_t *blk, size_t len)
{
return filestream_write((RFILE*)ud, blk, (int64_t)len) == (int64_t)len;
}
static int bz2_parse_file_init(file_archive_transfer_t *state,
const char *file)
{
uint8_t magic[4];
struct bz2_file_context *ctx;
state->context = NULL;
if (state->archive_size < 4)
return -1;
filestream_seek(state->archive_file, 0, RETRO_VFS_SEEK_POSITION_START);
if ( filestream_read(state->archive_file, magic, 4) != 4
|| memcmp(magic, "BZh", 3) || magic[3] < '1' || magic[3] > '9')
return -1;
if (!(ctx = (struct bz2_file_context*)calloc(1, sizeof(*ctx))))
return -1;
bz2_inner_filename(file, ctx->inner_filename, sizeof(ctx->inner_filename));
ctx->state = state;
state->context = ctx;
state->step_total = 1;
return 0;
}
static int bz2_parse_file_iterate_step(void *context,
const char *valid_exts,
struct archive_extract_userdata *userdata, file_archive_file_cb file_cb)
{
struct bz2_file_context *ctx = (struct bz2_file_context*)context;
struct bz2_whole w;
if (!ctx || ctx->parsed)
return 0;
ctx->parsed = true;
memset(&w, 0, sizeof(w));
w.max = ARCHIVE_BZ2_KEEP;
if ( !bz2_pass(&ctx->src, ctx->state->archive_file,
ctx->state->archive_size, bz2_gather, &w)
|| w.total > UINT32_MAX
|| w.total > (uint64_t)((size_t)-1))
{
free(w.data);
return -1;
}
ctx->data = w.data;
ctx->size = (size_t)w.total;
strlcpy(userdata->current_file_path, ctx->inner_filename,
sizeof(userdata->current_file_path));
userdata->crc = w.crc;
userdata->size = w.total;
if (file_cb && !file_cb(ctx->inner_filename, valid_exts, NULL, 0,
(uint32_t)ctx->state->archive_size, (uint32_t)ctx->size,
w.crc, userdata))
return 0;
return 1;
}
static void bz2_parse_file_free(void *context)
{
struct bz2_file_context *ctx = (struct bz2_file_context*)context;
if (!ctx)
return;
bz2_src_free(&ctx->src);
free(ctx->data);
free(ctx);
}
static bool bz2_stream_decompress_data_to_file_init(
void *context, file_archive_file_handle_t *handle,
const uint8_t *cdata, unsigned cmode, uint64_t csize, uint64_t size)
{
struct bz2_file_context *ctx = (struct bz2_file_context*)context;
(void)handle;
(void)cdata;
(void)cmode;
(void)csize;
(void)size;
if (!ctx)
return false;
ctx->streaming = false;
if (ctx->data || !ctx->state->pending_sink)
return true;
ctx->streaming = true;
return bz2_src_begin(&ctx->src, ctx->state->archive_file,
ctx->state->archive_size);
}
/* The member kept from its listing; or to the file a block a call; or
* else decoded whole, where it was listed too big to keep */
static int bz2_stream_decompress_data_to_file_iterate(
void *context, file_archive_file_handle_t *handle)
{
struct bz2_file_context *ctx = (struct bz2_file_context*)context;
if (!ctx || !handle)
return -1;
if (ctx->streaming)
{
const uint8_t *blk;
size_t len;
int e = bz2_src_next(&ctx->src, &blk, &len);
if (e == RBZIP2_PENDING)
return bz2_write(ctx->state->pending_sink, blk, len) ? 0 : -1;
ctx->streaming = false;
handle->data = NULL;
return e == RBZIP2_OK ? 1 : -1;
}
if (!ctx->data)
{
struct bz2_whole w;
memset(&w, 0, sizeof(w));
w.max = (size_t)-1;
if ( !bz2_pass(&ctx->src, ctx->state->archive_file,
ctx->state->archive_size, bz2_gather, &w)
|| w.total != ctx->size)
{
free(w.data);
return -1;
}
ctx->data = w.data;
}
handle->data = ctx->data;
return 1;
}
/* The member of @path, named @needle, into *@buf (NUL after it) or the
* file @optional_outfile, a block at a time; returns its size, or -1. */
static int64_t bz2_file_read(const char *path, const char *needle,
void **buf, const char *optional_outfile)
{
char inner[PATH_MAX_LENGTH];
struct bz2_src src;
bool ok;
int64_t size = -1;
RFILE *file;
bz2_inner_filename(path, inner, sizeof(inner));
if (!string_is_equal(inner, needle))
return -1;
if (!(file = filestream_open(path, RETRO_VFS_FILE_ACCESS_READ,
file_archive_open_hints(path_get_size(path)))))
return -1;
memset(&src, 0, sizeof(src));
if (optional_outfile)
{
RFILE *out = filestream_open(optional_outfile,
RETRO_VFS_FILE_ACCESS_WRITE, RETRO_VFS_FILE_ACCESS_HINT_NONE);
ok = out && bz2_pass(&src, file, filestream_get_size(file),
bz2_write, out);
if (out)
{
if (ok)
size = filestream_tell(out);
if (filestream_close(out) != 0)
size = -1;
/* no part of a member that did not decode is left */
if (size < 0)
filestream_delete(optional_outfile);
}
}
else
{
struct bz2_whole w;
memset(&w, 0, sizeof(w));
w.max = (size_t)-1;
ok = bz2_pass(&src, file, filestream_get_size(file), bz2_gather,
&w);
if (ok && (w.data || (w.data = (uint8_t*)malloc(1))))
{
w.data[w.len] = '\0';
*buf = w.data;
size = (int64_t)w.len;
}
else
free(w.data);
}
bz2_src_free(&src);
filestream_close(file);
return size;
}
const struct file_archive_file_backend bz2_backend = {
bz2_parse_file_init,
bz2_parse_file_iterate_step,
bz2_parse_file_free,
bz2_stream_decompress_data_to_file_init,
bz2_stream_decompress_data_to_file_iterate,
encoding_crc32,
bz2_file_read,
"bzip2"
};
+57 -29
View File
@@ -50,13 +50,30 @@
struct rar_context_t
{
rrar_archive_t *archive;
file_archive_transfer_t *state;
/* The last extracted member, owned here so the handle handed to the
* caller stays valid until the next call. */
uint8_t *output;
uint32_t parse_index;
uint32_t decompress_index;
bool to_sink; /* the member goes to the file as it comes */
};
/* A member bound for a file, bigger than this, goes to it as it is
* unpacked rather than whole through memory */
#define RAR_SINK_OVER (256 * 1024)
static int rar_sink_write(void *ud, const uint8_t *data, size_t len)
{
return filestream_write((RFILE*)ud, data, (int64_t)len) != (int64_t)len;
}
static int rar_crc_write(void *ud, const uint8_t *data, size_t len)
{
*(uint32_t*)ud = encoding_crc32(*(uint32_t*)ud, data, len);
return 0;
}
static int rar_open_archive(struct rar_context_t *ctx,
file_archive_transfer_t *state)
{
@@ -99,7 +116,7 @@ typedef struct
static int rar_file_decompressed(
const char *name, const char *valid_exts,
const uint8_t *cdata, unsigned cmode,
uint32_t csize, uint32_t size,
uint64_t csize, uint64_t size,
uint32_t crc32, struct archive_extract_userdata *userdata)
{
rar_decomp_state_t *st = (rar_decomp_state_t*)userdata->cb_data;
@@ -130,16 +147,12 @@ static int rar_file_decompressed(
&member, &member_len) != RRAR_OK)
return 1;
/* RetroArch expects a NUL after the data. */
if ((*st->buf = malloc(member_len + 1)))
{
((char*)(*st->buf))[member_len] = '\0';
if (member_len)
memcpy(*st->buf, member, member_len);
st->size = member_len;
st->found = true;
}
free(member);
/* the caller's to keep, with a NUL after the data in the octet
* to spare */
member[member_len] = '\0';
*st->buf = member;
st->size = member_len;
st->found = true;
}
return 0;
@@ -203,20 +216,20 @@ static int64_t rar_file_read(
static bool rar_stream_decompress_data_to_file_init(
void *context, file_archive_file_handle_t *handle,
const uint8_t *cdata, unsigned cmode, uint32_t csize, uint32_t size)
const uint8_t *cdata, unsigned cmode, uint64_t csize, uint64_t size)
{
struct rar_context_t *ctx = (struct rar_context_t*)context;
(void)handle;
(void)cmode;
(void)csize;
(void)size;
if (!ctx)
return false;
/* The parse step hands the entry index through cdata. */
ctx->decompress_index = (uint32_t)(size_t)cdata;
ctx->to_sink = ctx->state->pending_sink && size > RAR_SINK_OVER;
return true;
}
@@ -229,9 +242,22 @@ static int rar_stream_decompress_data_to_file_iterate(
uint8_t *member = NULL;
size_t member_len = 0;
if ( !ctx || !ctx->archive
|| rrar_archive_extract(ctx->archive, ctx->decompress_index,
&member, &member_len) != RRAR_OK)
if (!ctx || !ctx->archive)
return -1;
/* in the file as it comes, whole */
if (ctx->to_sink)
{
if (rrar_archive_extract_sink(ctx->archive, ctx->decompress_index,
rar_sink_write, ctx->state->pending_sink) != RRAR_OK)
return -1;
if (handle)
handle->data = NULL;
return 1;
}
if (rrar_archive_extract(ctx->archive, ctx->decompress_index,
&member, &member_len) != RRAR_OK)
return -1;
/* The caller reads through handle->data until it calls back in, so
@@ -268,6 +294,7 @@ static int rar_parse_file_init(file_archive_transfer_t *state,
goto error;
state->context = ctx;
ctx->state = state;
if (!rar_open_archive(ctx, state))
goto error;
@@ -301,29 +328,30 @@ static int rar_parse_file_iterate_step(void *context,
entry = rrar_archive_entry(ctx->archive, ctx->parse_index);
/* Directories, members this reader cannot unpack (solid, encrypted,
* an older packer) and members of 4 GiB or more, which the uint32_t
* sizes here cannot describe, are passed over: the rest of the
/* Directories and members this reader cannot unpack (solid,
* encrypted, an older packer) are passed over: the rest of the
* archive stays usable. */
if ( entry
&& !entry->is_dir
&& entry->supported
&& entry->size <= (uint64_t)0xFFFFFFFFu)
if (entry && !entry->is_dir && entry->supported)
{
uint32_t size = (uint32_t)entry->size;
strlcpy(userdata->current_file_path, entry->name,
sizeof(userdata->current_file_path));
userdata->crc = entry->crc;
userdata->size = size;
userdata->size = entry->size;
/* A RAR 5 member may have a BLAKE2 hash in place of its CRC */
if (userdata->crc_wanted && !entry->has_crc)
{
userdata->crc = 0;
if (rrar_archive_extract_sink(ctx->archive, ctx->parse_index,
rar_crc_write, &userdata->crc) != RRAR_OK)
return -1;
}
/* The entry index travels in cdata; the packed size is reported
* only for progress. */
if (file_cb && !file_cb(userdata->current_file_path, valid_exts,
(const uint8_t*)(size_t)ctx->parse_index, 0,
entry->packed_size > (uint64_t)0xFFFFFFFFu
? 0xFFFFFFFFu : (uint32_t)entry->packed_size,
size, entry->crc, userdata))
entry->packed_size, entry->size, userdata->crc, userdata))
return 0;
}
+912 -163
View File
File diff suppressed because it is too large. Load diff
+154 -89
View File
@@ -45,13 +45,20 @@
#define ZSTD_MAGIC "\x28\xB5\x2F\xFD"
#define ZSTD_MAGIC_LEN 4
/* Bigger than this, a file bound for disk is written out a block at a
* time */
#define ZSTD_STREAM_OVER (256 * 1024)
struct zstd_file_context
{
file_archive_transfer_t *state;
uint8_t *decompressed_data;
rzstd_blocks_t *zs; /* the frame going out a block at a time */
uint8_t *in; /* a block of it read, where it is not lent */
int64_t in_at; /* where the next is read from */
char inner_filename[PATH_MAX_LENGTH];
char archive_path[PATH_MAX_LENGTH];
int64_t archive_size;
uint32_t decompressed_size;
size_t decompressed_size;
bool parsed;
};
@@ -67,6 +74,84 @@ static void zstd_derive_inner_filename(const char *path, char *s, size_t len)
((char*)ext)[0] = '\0';
}
/* The frame to be decoded a block at a time: from where the stream lends
* it whole, or else read a block at a time into a buffer of one */
static bool zstd_src_begin(struct zstd_file_context *ctx)
{
RFILE *f = ctx->state->archive_file;
const uint8_t *src = NULL;
rzstd_blocks_free(ctx->zs);
free(ctx->in);
ctx->zs = NULL;
ctx->in = NULL;
ctx->in_at = 0;
if ((uint64_t)ctx->archive_size <= (uint64_t)((size_t)-1))
{
filestream_seek(f, 0, RETRO_VFS_SEEK_POSITION_START);
if (filestream_view(f, ctx->archive_size, &src) == ctx->archive_size)
return (ctx->zs = rzstd_blocks_begin(src,
(size_t)ctx->archive_size)) != NULL;
}
return (ctx->in = (uint8_t*)malloc(RZSTD_WANT_MAX))
&& (ctx->zs = rzstd_blocks_open());
}
/* The frame's next decoded octets: RZSTD_PROCESS_NEXT with some, or
* RZSTD_PROCESS_END or RZSTD_PROCESS_ERROR */
static int zstd_src_next(struct zstd_file_context *ctx,
const uint8_t **data, size_t *len)
{
RFILE *f = ctx->state->archive_file;
*data = NULL;
*len = 0;
if (!ctx->in)
return rzstd_blocks_step(ctx->zs, data, len);
for (;;)
{
size_t want = rzstd_blocks_want(ctx->zs);
/* all of it out, and nothing after it */
if (!want)
return ctx->in_at == ctx->archive_size
? RZSTD_PROCESS_END : RZSTD_PROCESS_ERROR;
if (want > RZSTD_WANT_MAX)
return RZSTD_PROCESS_ERROR;
filestream_seek(f, ctx->in_at, RETRO_VFS_SEEK_POSITION_START);
if (filestream_read(f, ctx->in, (int64_t)want) != (int64_t)want)
return RZSTD_PROCESS_ERROR;
ctx->in_at += (int64_t)want;
if (rzstd_blocks_give(ctx->zs, ctx->in, want, data, len)
!= RZSTD_PROCESS_NEXT)
return RZSTD_PROCESS_ERROR;
if (*len)
return RZSTD_PROCESS_NEXT;
}
}
static void zstd_src_end(struct zstd_file_context *ctx)
{
rzstd_blocks_free(ctx->zs);
free(ctx->in);
ctx->zs = NULL;
ctx->in = NULL;
}
/* The CRC of what the frame holds, decoded a block at a time */
static bool zstd_frame_crc(struct zstd_file_context *ctx, uint32_t *crc)
{
const uint8_t *blk;
size_t len;
int e = RZSTD_PROCESS_ERROR;
*crc = 0;
if (zstd_src_begin(ctx))
while ((e = zstd_src_next(ctx, &blk, &len)) == RZSTD_PROCESS_NEXT)
*crc = encoding_crc32(*crc, blk, len);
zstd_src_end(ctx);
return e == RZSTD_PROCESS_END;
}
static int zstd_parse_file_init(file_archive_transfer_t *state,
const char *file)
{
@@ -90,8 +175,8 @@ static int zstd_parse_file_init(file_archive_transfer_t *state,
zstd_derive_inner_filename(file, ctx->inner_filename,
sizeof(ctx->inner_filename));
strlcpy(ctx->archive_path, file, sizeof(ctx->archive_path));
ctx->archive_size = state->archive_size;
ctx->state = state;
state->context = ctx;
state->step_total = 1;
@@ -147,13 +232,13 @@ static int zstd_parse_file_iterate_step(void *context,
|| content_size == ZSTD_SIZE_ERROR)
return -1;
/* decompressed_size is a uint32_t and feeds a later malloc; reject
* values that would truncate to a smaller size and mismatch the
* length the decode actually produces. */
if (content_size > UINT32_MAX)
/* decompressed_size feeds a later malloc; reject values that would
* truncate on a 32-bit host and mismatch the length the decode
* actually produces. */
if ((uint64_t)content_size > (uint64_t)SIZE_MAX)
return -1;
ctx->decompressed_size = (uint32_t)content_size;
ctx->decompressed_size = (size_t)content_size;
strlcpy(userdata->current_file_path, ctx->inner_filename,
sizeof(userdata->current_file_path));
@@ -161,10 +246,14 @@ static int zstd_parse_file_iterate_step(void *context,
userdata->crc = 0;
userdata->size = (uint64_t)content_size;
/* A frame holds no CRC of its content */
if (userdata->crc_wanted && !zstd_frame_crc(ctx, &userdata->crc))
return -1;
if (file_cb && !file_cb(ctx->inner_filename, valid_exts,
NULL, 0,
(uint32_t)state->archive_size, (uint32_t)content_size,
0, userdata))
(uint64_t)state->archive_size, (uint64_t)content_size,
userdata->crc, userdata))
return 0;
return 1;
@@ -177,71 +266,73 @@ static void zstd_parse_file_free(void *context)
return;
if (ctx->decompressed_data)
free(ctx->decompressed_data);
rzstd_blocks_free(ctx->zs);
free(ctx->in);
free(ctx);
}
static bool zstd_stream_decompress_data_to_file_init(
void *context, file_archive_file_handle_t *handle,
const uint8_t *cdata, unsigned cmode, uint32_t csize, uint32_t size)
const uint8_t *cdata, unsigned cmode, uint64_t csize, uint64_t size)
{
struct zstd_file_context *ctx = (struct zstd_file_context*)context;
if (!ctx)
return false;
return true;
if (!ctx->state->pending_sink || size <= ZSTD_STREAM_OVER)
return true;
return zstd_src_begin(ctx);
}
static int zstd_stream_decompress_data_to_file_iterate(
void *context, file_archive_file_handle_t *handle)
{
struct zstd_file_context *ctx = (struct zstd_file_context*)context;
void *compressed_data = NULL;
void *owned = NULL;
const uint8_t *src;
size_t result;
int e;
RFILE *file;
if (!ctx || !handle)
return -1;
if (ctx->zs)
{
/* a block to the file per call */
const uint8_t *blk;
size_t len;
e = zstd_src_next(ctx, &blk, &len);
if ( e == RZSTD_PROCESS_ERROR
|| (len && filestream_write(ctx->state->pending_sink, blk,
(int64_t)len) != (int64_t)len))
return -1;
if (e == RZSTD_PROCESS_NEXT)
return 0;
zstd_src_end(ctx);
handle->data = NULL;
return 1;
}
if (ctx->decompressed_data)
{
handle->data = ctx->decompressed_data;
return 1;
}
/* Read the entire compressed file */
file = filestream_open(ctx->archive_path,
RETRO_VFS_FILE_ACCESS_READ,
RETRO_VFS_FILE_ACCESS_HINT_NONE);
if (!file)
if (!(src = file_archive_whole(ctx->state->archive_file, ctx->archive_size,
&owned)))
return -1;
compressed_data = malloc((size_t)ctx->archive_size);
if (!compressed_data)
{
filestream_close(file);
return -1;
}
if (filestream_read(file, compressed_data, ctx->archive_size)
!= ctx->archive_size)
{
free(compressed_data);
filestream_close(file);
return -1;
}
filestream_close(file);
ctx->decompressed_data = (uint8_t*)malloc(ctx->decompressed_size);
if (!ctx->decompressed_data)
{
free(compressed_data);
free(owned);
return -1;
}
e = (rzstd_decode(ctx->decompressed_data, ctx->decompressed_size,
compressed_data, (size_t)ctx->archive_size, &result)
src, (size_t)ctx->archive_size, &result)
!= RZSTD_PROCESS_END);
free(compressed_data);
free(owned);
if (e)
{
@@ -263,56 +354,34 @@ static int64_t zstd_file_read(
const char *optional_outfile)
{
char inner_name[PATH_MAX_LENGTH];
int64_t file_size;
void *compressed_data = NULL;
uint8_t *decompressed = NULL;
int64_t content_size;
size_t result;
int e;
RFILE *file;
int64_t file_size;
int64_t content_size;
void *owned = NULL;
const uint8_t *src;
uint8_t *decompressed = NULL;
size_t result = 0;
int e = 1;
RFILE *file;
zstd_derive_inner_filename(path, inner_name, sizeof(inner_name));
if (!string_is_equal(inner_name, needle))
return -1;
file = filestream_open(path,
RETRO_VFS_FILE_ACCESS_READ,
RETRO_VFS_FILE_ACCESS_HINT_NONE);
if (!file)
if (!(file = filestream_open(path, RETRO_VFS_FILE_ACCESS_READ,
file_archive_open_hints(path_get_size(path)))))
return -1;
file_size = filestream_get_size(file);
if (file_size <= 0)
{
filestream_close(file);
return -1;
}
/* The frame is decoded where the stream lends it, so the stream
* stays open until it is */
if ( (file_size = filestream_get_size(file)) <= 0
|| !(src = file_archive_whole(file, file_size, &owned)))
goto done;
compressed_data = malloc((size_t)file_size);
if (!compressed_data)
{
filestream_close(file);
return -1;
}
if (filestream_read(file, compressed_data, (int64_t)file_size) != file_size)
{
free(compressed_data);
filestream_close(file);
return -1;
}
filestream_close(file);
content_size = rzstd_frame_content_size(compressed_data,
(size_t)file_size);
content_size = rzstd_frame_content_size(src, (size_t)file_size);
if ( content_size == ZSTD_SIZE_UNKNOWN
|| content_size == ZSTD_SIZE_ERROR)
{
free(compressed_data);
return -1;
}
goto done;
/* Reject sizes that would overflow the "+1" NUL-byte allocation or
* truncate when cast to size_t on 32-bit hosts. Without this guard
@@ -320,22 +389,18 @@ static int64_t zstd_file_read(
* malloc(0) may return a non-NULL pointer, and the following
* decode writes 4 GiB into it. */
if ((uint64_t)content_size >= (uint64_t)SIZE_MAX)
{
free(compressed_data);
return -1;
}
goto done;
decompressed = (uint8_t*)malloc((size_t)(content_size + 1));
if (!decompressed)
{
free(compressed_data);
return -1;
}
if (!(decompressed = (uint8_t*)malloc((size_t)(content_size + 1))))
goto done;
e = (rzstd_decode(decompressed, (size_t)content_size,
compressed_data, (size_t)file_size, &result)
src, (size_t)file_size, &result)
!= RZSTD_PROCESS_END);
free(compressed_data);
done:
free(owned);
filestream_close(file);
if (e)
{
+29 -11
View File
@@ -204,6 +204,9 @@ const char *path_get_archive_delim(const char *path)
/* Check ".rar" */
else if (c3 == 'r' && c2 == 'a' && c1 == 'r')
return delim;
/* Check ".bz2" */
else if (c3 == 'b' && c2 == 'z' && delim[-1] == '2')
return delim;
}
}
}
@@ -295,7 +298,11 @@ bool path_is_compressed_file(const char *path)
switch (tolower((unsigned char)ext[0]))
{
case '7':
return ext[1] == 'z' && ext[2] == '\0';
return tolower((unsigned char)ext[1]) == 'z' && ext[2] == '\0';
case 'b':
return tolower((unsigned char)ext[1]) == 'z'
&& ext[2] == '2'
&& ext[3] == '\0';
case 'a':
return tolower((unsigned char)ext[1]) == 'p'
&& tolower((unsigned char)ext[2]) == 'k'
@@ -1326,14 +1333,24 @@ size_t fill_pathname_abbreviated_or_relative(char *s,
size_t _len;
int rel_slashes;
const char *trimmed_path, *trimmed_base;
char abs_path[PATH_MAX_LENGTH];
char ref_path[PATH_MAX_LENGTH];
/* Both working paths on the heap, to keep the frame within the
* 2 KiB thread-stack budget. */
char *abs_path = (char*)malloc(2 * PATH_MAX_LENGTH);
char *ref_path;
if (!abs_path)
{
if (len)
*s = '\0';
return 0;
}
ref_path = abs_path + PATH_MAX_LENGTH;
/* Expand paths which start with :\ to an absolute path */
fill_pathname_expand_special(abs_path, in_path, sizeof(abs_path));
fill_pathname_expand_special(abs_path, in_path, PATH_MAX_LENGTH);
pathname_conform_slashes_to_os(abs_path);
strlcpy(ref_path, in_refpath, sizeof(ref_path));
strlcpy(ref_path, in_refpath, PATH_MAX_LENGTH);
pathname_conform_slashes_to_os(ref_path);
/* Resolve a relative path against the referencing file's directory.
@@ -1342,12 +1359,12 @@ size_t fill_pathname_abbreviated_or_relative(char *s,
* (only its directory is needed for the join) and never in @s. */
if (!path_is_absolute(abs_path))
{
_len = fill_pathname_basedir(ref_path, ref_path, sizeof(ref_path));
strlcpy(ref_path + _len, abs_path, sizeof(ref_path) - _len);
path_resolve_realpath(ref_path, sizeof(ref_path), false);
strlcpy(abs_path, ref_path, sizeof(abs_path));
_len = fill_pathname_basedir(ref_path, ref_path, PATH_MAX_LENGTH);
strlcpy(ref_path + _len, abs_path, PATH_MAX_LENGTH - _len);
path_resolve_realpath(ref_path, PATH_MAX_LENGTH, false);
strlcpy(abs_path, ref_path, PATH_MAX_LENGTH);
pathname_conform_slashes_to_os(abs_path);
strlcpy(ref_path, in_refpath, sizeof(ref_path));
strlcpy(ref_path, in_refpath, PATH_MAX_LENGTH);
pathname_conform_slashes_to_os(ref_path);
}
@@ -1363,7 +1380,8 @@ size_t fill_pathname_abbreviated_or_relative(char *s,
/* Use the shortest path, preferring the relative path */
if (rel_slashes <= get_pathname_num_slashes(s))
return path_relative_to(s, abs_path, ref_path, len);
_len = path_relative_to(s, abs_path, ref_path, len);
free(abs_path);
return _len;
}
+1542 -152
View File
File diff suppressed because it is too large. Load diff
+103
View File
@@ -255,6 +255,109 @@ int r7z_bcj2_decode_part(r7z_bcj2_state_t *st,
return RBCJ2_OK;
}
/* Whether @in has @n octets left, or is all there is */
#define BCJ2_HAS(in, n) ((in)->len - (in)->pos >= (n) || !(in)->more)
int r7z_bcj2_decode_in(r7z_bcj2_state_t *st,
uint8_t *dst, size_t dst_cap, size_t *wrote,
r7z_bcj2_in_t *main_in, r7z_bcj2_in_t *call_in,
r7z_bcj2_in_t *jump_in, r7z_bcj2_in_t *rc_in)
{
size_t pos = 0;
*wrote = 0;
if (!st || dst_cap <= 4)
return RBCJ2_ERROR_PARAM;
if (!st->started)
{
const uint8_t *r = rc_in->p + rc_in->pos;
if (!BCJ2_HAS(rc_in, 5))
return RBCJ2_PENDING;
if (rc_in->len - rc_in->pos < 5 || r[0] != 0)
return RBCJ2_ERROR_DATA;
st->code = ((uint32_t)r[1] << 24) | ((uint32_t)r[2] << 16)
| ((uint32_t)r[3] << 8) | (uint32_t)r[4];
if (st->code == 0xFFFFFFFFu)
return RBCJ2_ERROR_DATA;
st->range = 0xFFFFFFFFu;
rc_in->pos += 5;
st->started = 1;
}
/* with room for a converted branch's four octets */
while (pos < dst_cap - 4 && main_in->pos < main_in->len)
{
uint8_t b = main_in->p[main_in->pos];
uint32_t idx, bound, ttt, val;
r7z_bcj2_in_t *t;
if (!IS_E8_OR_E9(b) && !IS_NEAR_JMP(st->prev, b))
{
dst[pos++] = b;
main_in->pos++;
st->ip++;
st->prev = b;
continue;
}
/* a candidate: what its bit and its target may take is there, or
* it waits, unread, for more */
t = b == 0xE8 ? call_in : jump_in;
if (!BCJ2_HAS(rc_in, 1) || !BCJ2_HAS(t, 4))
break;
dst[pos++] = b;
main_in->pos++;
st->ip++;
if (b == 0xE8)
idx = 2 + (uint32_t)st->prev;
else if (b == 0xE9)
idx = 1;
else
idx = 0;
if (st->range < TOP_VALUE)
{
if (rc_in->pos == rc_in->len)
return RBCJ2_ERROR_DATA;
st->range <<= 8;
st->code = (st->code << 8) | (uint32_t)rc_in->p[rc_in->pos++];
}
ttt = (uint32_t)st->probs[idx];
bound = (st->range >> NUM_MODEL_BITS) * ttt;
if (st->code < bound)
{
st->range = bound;
st->probs[idx] = (uint16_t)(ttt
+ ((BIT_MODEL_TOTAL - ttt) >> NUM_MOVE_BITS));
st->prev = b;
continue;
}
st->range -= bound;
st->code -= bound;
st->probs[idx] = (uint16_t)(ttt - (ttt >> NUM_MOVE_BITS));
if (t->len - t->pos < 4)
return RBCJ2_ERROR_DATA;
val = ((uint32_t)t->p[t->pos] << 24)
| ((uint32_t)t->p[t->pos + 1] << 16)
| ((uint32_t)t->p[t->pos + 2] << 8)
| (uint32_t)t->p[t->pos + 3];
t->pos += 4;
st->ip += 4;
val -= st->ip;
dst[pos] = (uint8_t)( val & 0xFF);
dst[pos + 1] = (uint8_t)((val >> 8) & 0xFF);
dst[pos + 2] = (uint8_t)((val >> 16) & 0xFF);
dst[pos + 3] = (uint8_t)((val >> 24) & 0xFF);
pos += 4;
st->prev = (uint8_t)((val >> 24) & 0xFF);
}
*wrote = pos;
return RBCJ2_PENDING;
}
int r7z_bcj2_decode(uint8_t *dst, size_t dst_len,
const uint8_t *main_buf, size_t main_len,
const uint8_t *call_buf, size_t call_len,
+35
View File
@@ -398,3 +398,38 @@ size_t rfilters_ia64_decode(uint32_t ip, uint8_t *data, size_t len)
return i;
}
size_t rfilters_arm64_decode(uint32_t ip, uint8_t *data, size_t len)
{
size_t i;
len &= ~(size_t)3;
for (i = 0; i < len; i += 4)
{
uint32_t pc = ip + (uint32_t)i;
uint32_t v = rf_get_le32(data + i);
if ((v >> 26) == 0x25)
{
/* BL: a 26-bit word offset */
v = 0x94000000 | ((v - (pc >> 2)) & 0x03FFFFFF);
rf_set_le32(data + i, v);
}
else if ((v & 0x9F000000) == 0x90000000)
{
/* ADRP: a 21-bit page offset, converted only within +-512 MiB,
* the range one test of the sum covers */
uint32_t src = ((v >> 29) & 3) | ((v >> 3) & 0x001FFFFC);
uint32_t dst;
if ((src + 0x00020000) & 0x001C0000)
continue;
dst = src - (pc >> 12);
v &= 0x9000001F;
v |= (dst & 3) << 29;
v |= (dst & 0x0003FFFC) << 3;
v |= (0u - (dst & 0x00020000)) & 0x00E00000;
rf_set_le32(data + i, v);
}
}
return len;
}
+26 -11
View File
@@ -343,7 +343,7 @@ static dummy_res_t try_dummy(const rlzma_stream_t *s,
const uint16_t *prob;
dummy_res_t res;
pos_state = ((uint32_t)(s->dic_pos) & (((uint32_t)1 << s->pb) - 1)) << 4;
pos_state = ((uint32_t)(s->total_pos) & (((uint32_t)1 << s->pb) - 1)) << 4;
prob = probs + IS_MATCH + COMBINED_PS_STATE;
RC_IF_BIT_0_CHECK(prob)
@@ -357,9 +357,10 @@ static dummy_res_t try_dummy(const rlzma_stream_t *s,
lit_mask = ((uint32_t)0x100 << s->lp) - ((uint32_t)0x100 >> s->lc);
lit_state = 0;
if (s->dic_pos != 0)
lit_state = ((((uint32_t)s->dic_pos << 8)
+ (uint32_t)s->dic[s->dic_pos - 1]) & lit_mask) << s->lc;
if (s->total_pos != 0)
lit_state = ((((uint32_t)s->total_pos << 8)
+ (uint32_t)s->dic[(s->dic_pos ? s->dic_pos : s->dic_size)
- 1]) & lit_mask) << s->lc;
prob = probs + LITERAL + (uint32_t)3 * lit_state;
@@ -573,7 +574,7 @@ static int decode_real(rlzma_stream_t *s, size_t dic_limit,
do
{
uint16_t *prob;
uint32_t pos_state = ((uint32_t)dic_pos & pb_mask) << 4;
uint32_t pos_state = ((uint32_t)total_pos & pb_mask) << 4;
uint32_t len;
prob = probs_is_match + COMBINED_PS_STATE;
@@ -585,12 +586,21 @@ static int decode_real(rlzma_stream_t *s, size_t dic_limit,
RC_UPDATE_0(prob)
/* The first byte of the window has no predecessor; its context
* is zero, exactly as in the one-shot decoder. */
/* The stream's first byte, or the first since the dictionary
* was reset, has no predecessor; its context is zero, exactly
* as in the one-shot decoder. Positions count from there,
* wherever in the window it is; the byte before the window's
* start is its end. */
lit_state = 0;
if (dic_pos != 0)
lit_state = ((((uint32_t)dic_pos << 8)
+ (uint32_t)dic[dic_pos - 1]) & lit_mask) << lc;
if (total_pos != 0)
{
if (dic_pos != 0)
lit_state = ((((uint32_t)total_pos << 8)
+ (uint32_t)dic[dic_pos - 1]) & lit_mask) << lc;
else
lit_state = ((((uint32_t)total_pos << 8)
+ (uint32_t)dic[dic_size - 1]) & lit_mask) << lc;
}
/* The masked context is already scaled by 0x100, so the stride
* is 3 (one literal slot), not the full 0x300 table. */
@@ -1004,9 +1014,11 @@ void rlzma_stream_reset_parts(rlzma_stream_t *s, int init_dic, int init_state)
* state while continuing the previous chunk's dictionary, or reset
* the dictionary while the range coder carries on. Collapsing them
* into one operation would make those chunk types undecodable. */
/* A dictionary reset forgets what came before, which positions
* then count from; where the window is written next stays, so what
* the caller has not read of it yet is still there. */
if (init_dic)
{
s->dic_pos = 0;
s->total_pos = 0;
s->got_marker = 0;
}
@@ -1033,6 +1045,9 @@ void rlzma_stream_reset_parts(rlzma_stream_t *s, int init_dic, int init_state)
void rlzma_stream_reset(rlzma_stream_t *s)
{
if (!s)
return;
s->dic_pos = 0;
rlzma_stream_reset_parts(s, 1, 1);
}
+8 -6
View File
@@ -170,12 +170,14 @@
/* Container limits. A hunk is bounded by the format; the map and
* metadata bounds are ours, sized well above anything a real image
* carries, so a corrupt header cannot ask for an unbounded allocation. */
/* What a hunk may measure, which the format sets differently either
* side of version 5: half a megabyte from version 5, and sixteen
* megabytes before it. A single ceiling is wrong both ways -- too
* generous for version 5 and too mean for the versions that allow more,
* where it would refuse an image another reader accepts. */
#define RCHD_MAX_HUNK_BYTES_V5 (512 * 1024)
/* What a hunk may measure, which differs either side of version 5:
* one megabyte from version 5 -- the largest hunk chdman will write
* (its HUNK_SIZE_MAX) -- and sixteen megabytes before it. A single
* ceiling is wrong both ways -- too generous for version 5 and too mean
* for the versions that allow more, where it would refuse an image
* another reader accepts. Half a megabyte here refused every version 5
* CD image made with a hunk above 524288 bytes. */
#define RCHD_MAX_HUNK_BYTES_V5 (1024 * 1024)
#define RCHD_MAX_HUNK_BYTES_OLD (16 * 1024 * 1024)
#define RCHD_MAX_MAP_BYTES (64 * 1024 * 1024)
#define RCHD_MAX_METADATA (16 * 1024 * 1024)
+11
View File
@@ -5494,6 +5494,17 @@ static int rh265_hw_show(rh265_hw_t *hw, bool drain)
int ret, slot;
if (hw->session.broken)
return -1;
if (drain && hw->session.ops.drain)
{
ret = hw->session.ops.drain(hw->session.ops.decoder);
if (ret < 0)
{
hw->session.broken = 1;
return -1;
}
if (ret > 0)
return 2;
}
slot = rh265_hw_output_pick(&hw->session.state, hw->session.max_reorder,
drain || hw->session.pressure);
if (slot < 0)
+279 -10
View File
@@ -45,6 +45,7 @@
#include <formats/rvp9.h>
#include <formats/rvp9_config.h>
#include <formats/rvp9_color.h>
#include <formats/rvp9_hw.h>
#endif
/* The 10-bit / HDR I420->RGB blits are shared (image_hdr_blit.c) but still
* declared in rwebm_video.h; the implementation is demuxer-independent.
@@ -59,6 +60,8 @@
#include <formats/video_hw_frame.h>
#ifdef HAVE_RAV1
#include <formats/rav1_hw_stream.h>
#endif
#if defined(HAVE_RAV1) || defined(HAVE_RVP9)
#include <features/features_cpu.h>
#endif
@@ -87,6 +90,15 @@ struct rmp4_video_stream
rvp8_video *vp8;
#ifdef HAVE_RVP9
rvp9_dec *vp9;
rvp9_hw_session_t *vp9_hw;
rvp9_hw_backend_t vp9_backend;
rvp9_hdr vp9_probe_header, vp9_initial_header;
int vp9_initial_valid;
video_color_metadata_t vp9_native_color;
size_t vp9_sizes[RMP4_VIDEO_MAX_SUPER], vp9_offset;
uint32_t vp9_sample;
int vp9_active, vp9_count, vp9_index, vp9_last_show;
int vp9_submitted, vp9_discard, vp9_restart;
video_color_metadata_t vp9_color;
int vp9_have_config;
rvp9_config_t vp9_config;
@@ -156,7 +168,7 @@ struct rmp4_video_stream
int is_fp16; /* last decoded frame written so */
int emit_argb; /* emit ARGB words instead of the default
R,G,B,A memory order (8-bit paths) */
int want_hw; /* H.264/HEVC/AV1 on the hardware backend */
int want_hw; /* H.264/HEVC/VP9/AV1 on the hardware backend */
/* The hardware frame of the picture the last step produced
* (rndr_kind 5), and whether the caller has yet to take it: one
* passed over is given back to the decoder. */
@@ -712,11 +724,10 @@ static bool rmp4_video_vp9_configure(rmp4_video_stream_t *s,
return true;
}
static bool rmp4_video_vp9_color(rmp4_video_stream_t *s)
static bool rmp4_video_vp9_header_color(const rmp4_video_stream_t *s,
const rvp9_hdr *header, video_color_metadata_t *color)
{
const rvp9_hdr *header = &s->vp9->hd;
const rvp9_config_t *config = &s->vp9_config;
video_color_metadata_t color;
if (s->vp9_have_config
&& (config->profile != header->profile
|| config->bit_depth != header->bit_depth
@@ -724,7 +735,13 @@ static bool rmp4_video_vp9_color(rmp4_video_stream_t *s)
|| header->subsampling_x != (config->chroma != 3)
|| header->subsampling_y != (config->chroma < 2)))
return false;
if (!rvp9_color_resolve(header, &s->vp9_color, &color))
return rvp9_color_resolve(header, &s->vp9_color, color);
}
static bool rmp4_video_vp9_color(rmp4_video_stream_t *s)
{
video_color_metadata_t color;
if (!rmp4_video_vp9_header_color(s, &s->vp9->hd, &color))
return false;
s->primaries = color.primaries;
s->transfer = color.transfer;
@@ -734,6 +751,73 @@ static bool rmp4_video_vp9_color(rmp4_video_stream_t *s)
}
#endif
#ifdef HAVE_RVP9
static bool rmp4_video_vp9_open_hw(rmp4_video_stream_t *s)
{
rvp9_hw_session_t *candidate;
rvp9_hw_backend_t backend;
rvp9_hw_ops_t ops;
video_color_metadata_t color;
rmp4_packet packet;
size_t sizes[RMP4_VIDEO_MAX_SUPER];
int found;
if (!rmp4_video_hw_color_allowed(s))
return false;
candidate = (rvp9_hw_session_t *)calloc(1, sizeof(*candidate));
if (!candidate)
return false;
found = s->vp9_initial_valid;
if (found)
candidate->pending.header = s->vp9_initial_header;
else
{
found = rmp4_select_track_sample(s->demux, s->track, 0)
&& rmp4_read_packet(s->demux, &packet) == 1;
rmp4_rewind(s->demux);
if (found)
{
rmp4_video_vp9_superframe(packet.data, packet.size,
sizes, RMP4_VIDEO_MAX_SUPER);
found = sizes[0] && !rvp9_parse_frame_header(packet.data, sizes[0],
NULL, NULL, &candidate->pending.header);
}
if (found)
{
s->vp9_initial_header = candidate->pending.header;
s->vp9_initial_valid = 1;
}
}
if (!found || candidate->pending.header.frame_type
|| candidate->pending.header.w != (int)s->width
|| candidate->pending.header.h != (int)s->height
|| !rmp4_video_vp9_header_color(s,
&candidate->pending.header, &color)
|| ((color.transfer == 16 || color.transfer == 18)
&& candidate->pending.header.bit_depth != 10)
|| !rvp9_hw_backend_open_config(&candidate->pending.header,
&color, s->vp9_have_config ? &s->vp9_config : NULL,
10, &ops, &backend))
{
free(candidate);
return false;
}
if (!rvp9_hw_session_init_mode(candidate, &ops, 10, backend.packet_decoder))
{
backend.close(&ops);
free(candidate);
return false;
}
s->vp9_hw = candidate;
s->vp9_backend = backend;
s->vp9_native_color = color;
s->primaries = color.primaries;
s->transfer = color.transfer;
s->matrix = color.matrix;
s->range = color.range == 2;
return true;
}
#endif
static bool rmp4_video_stream_open_decoder(rmp4_video_stream_t *s)
{
switch (s->codec)
@@ -748,6 +832,8 @@ static bool rmp4_video_stream_open_decoder(rmp4_video_stream_t *s)
return true;
#ifdef HAVE_RVP9
case RMP4_CODEC_VP9:
if (s->want_hw && rmp4_video_vp9_open_hw(s))
return true;
if (!(s->vp9 = (rvp9_dec*)calloc(1, sizeof(*s->vp9))))
return false;
rvp9_set_tile_pool(s->vp9, s->blit_pool, s->blit_bands);
@@ -853,6 +939,16 @@ static void rmp4_video_stream_close_decoder(rmp4_video_stream_t *s)
s->vp8 = NULL;
}
#ifdef HAVE_RVP9
s->vp9_active = 0;
s->vp9_sample = 0;
s->vp9_restart = 0;
if (s->vp9_hw)
{
rvp9_hw_session_cancel(s->vp9_hw);
s->vp9_backend.close(&s->vp9_hw->ops);
free(s->vp9_hw);
s->vp9_hw = NULL;
}
if (s->vp9)
{
rvp9_free(s->vp9);
@@ -1362,6 +1458,118 @@ static int rmp4_video_av1_output(rmp4_video_stream_t *s, int discard, int termin
/* Decode one demuxed packet into s->frame. Returns 1 when a picture was
* produced, 0 when the packet decoded but is not displayed, -1 on a
* decode error. */
#ifdef HAVE_RVP9
static int rmp4_video_vp9_hw_packet(rmp4_video_stream_t *s,
const rmp4_packet *packet, int discard)
{
int ret;
if (s->vp9_restart)
{
ret = s->vp9_backend.restart(s->vp9_hw->ops.decoder);
if (ret)
return ret > 0 ? 2 : -1;
s->vp9_restart = 0;
}
if (!s->vp9_active)
{
size_t offset = 0;
int i;
s->vp9_count = rmp4_video_vp9_superframe(packet->data,
packet->size, s->vp9_sizes, RMP4_VIDEO_MAX_SUPER);
s->vp9_last_show = -1;
for (i = 0; i < s->vp9_count; i++)
{
if (!s->vp9_sizes[i] || s->vp9_sizes[i] > packet->size - offset)
return -1;
if (packet->data[offset] & 0x0a)
s->vp9_last_show = i;
offset += s->vp9_sizes[i];
}
s->vp9_index = s->vp9_submitted = 0;
s->vp9_offset = 0;
s->vp9_discard = discard;
s->vp9_active = 1;
}
if (s->vp9_discard != discard)
return -1;
if (s->vp9_index < s->vp9_count)
{
if (!s->vp9_submitted)
{
if (!s->vp9_hw->phase)
{
video_color_metadata_t color;
int dims[RVP9_REF_FRAMES * 2];
unsigned i;
for (i = 0; i < RVP9_REF_FRAMES; i++)
{
dims[i * 2] = s->vp9_hw->reference_slots[i] >= 0
? s->vp9_hw->header.w : 0;
dims[i * 2 + 1] = s->vp9_hw->reference_slots[i] >= 0
? s->vp9_hw->header.h : 0;
}
ret = rvp9_parse_frame_header(packet->data + s->vp9_offset,
s->vp9_sizes[s->vp9_index], &s->vp9_hw->header,
dims, &s->vp9_probe_header);
if (ret < 0 || (!ret
&& (!rmp4_video_vp9_header_color(s, &s->vp9_probe_header, &color)
|| color.primaries != s->vp9_native_color.primaries
|| color.transfer != s->vp9_native_color.transfer
|| color.matrix != s->vp9_native_color.matrix
|| color.range != s->vp9_native_color.range)))
return -1;
}
ret = rvp9_hw_session_submit(s->vp9_hw,
packet->data + s->vp9_offset, s->vp9_sizes[s->vp9_index]);
if (ret > 0)
return 2;
if (ret < 0)
return -1;
s->vp9_submitted = 1;
}
if (s->vp9_hw->output_slot >= 0
&& (discard || s->vp9_index < s->vp9_last_show))
rvp9_hw_session_discard_output(s->vp9_hw);
s->vp9_offset += s->vp9_sizes[s->vp9_index++];
s->vp9_submitted = 0;
/* Yield between coded pictures, retaining the packet's input floor. */
if (s->vp9_index < s->vp9_count)
return 4;
}
if (s->vp9_backend.drain && s->vp9_sample + 1 >= (uint32_t)s->num_frames)
{
ret = s->vp9_backend.drain(s->vp9_hw->ops.decoder, discard != 0);
if (ret < 0)
return -1;
}
if (s->vp9_hw->output_slot >= 0)
{
video_hw_frame_t frame;
ret = rvp9_hw_session_output(s->vp9_hw, &frame);
if (ret > 0)
return 2;
if (ret < 0)
return -1;
frame.color = s->vp9_native_color;
if (frame.color.matrix == 1)
frame.yuv |= IMAGE_YUV_FLAG_BT709;
else
frame.yuv &= ~IMAGE_YUV_FLAG_BT709;
if (frame.color.range == 2)
frame.yuv |= IMAGE_YUV_FLAG_FULL_RANGE;
else
frame.yuv &= ~IMAGE_YUV_FLAG_FULL_RANGE;
rmp4_video_hwf_drop(s);
s->hwf = frame;
s->hwf_untaken = 1;
s->rndr_kind = 5;
}
s->vp9_active = 0;
s->vp9_sample++;
return s->vp9_last_show >= 0 ? 1 : 0;
}
#endif
static int rmp4_video_decode_packet(rmp4_video_stream_t *s,
const rmp4_packet *pkt, int discard)
{
@@ -1375,6 +1583,10 @@ static int rmp4_video_decode_packet(rmp4_video_stream_t *s,
return rmp4_video_av1_packet(s, pkt, discard);
#endif
s->rndr_kind = 0;
#ifdef HAVE_RVP9
if (s->vp9_hw)
return rmp4_video_vp9_hw_packet(s, pkt, discard);
#endif
/* A decoder can be absent if the re-open in rewind hit OOM. */
if (!s->vp8
#ifdef HAVE_RVP9
@@ -1529,6 +1741,9 @@ static int rmp4_video_stream_step(rmp4_video_stream_t *s,
{
rmp4_packet pkt;
int drained = -1;
#ifdef HAVE_RVP9
int vp9_eof = 0;
#endif
#ifdef HAVE_RAV1
int av1_eof = 0;
#endif
@@ -1574,6 +1789,9 @@ static int rmp4_video_stream_step(rmp4_video_stream_t *s,
if (r != 1)
{
if (r < 0) return -1;
#ifdef HAVE_RVP9
vp9_eof = r == 0;
#endif
#ifdef HAVE_RAV1
av1_eof = r == 0;
#endif
@@ -1637,6 +1855,26 @@ static int rmp4_video_stream_step(rmp4_video_stream_t *s,
s->completed = output == 0;
return -1;
}
#endif
#ifdef HAVE_RVP9
if (s->vp9_hw)
{
if (!vp9_eof)
return -1;
if (s->vp9_backend.drain)
{
int ret = s->vp9_backend.drain(s->vp9_hw->ops.decoder, discard != 0);
if (ret < 0)
return -1;
if (ret > 0)
{
s->waiting = RMP4_VIDEO_WAIT_DECODER;
return 2;
}
}
s->completed = 1;
return -1;
}
#endif
/* Out of packets. Display reordering can leave the last few pictures
* queued inside the H.264 decoder; hand them out before ending the
@@ -1701,8 +1939,9 @@ static int rmp4_video_stream_advance(rmp4_video_stream_t *s,
int *duration_ms, int discard)
{
int r;
#ifdef HAVE_RAV1
#if defined(HAVE_RAV1) || defined(HAVE_RVP9)
int64_t started = 0;
int bounded = 0;
#endif
if (!s)
@@ -1710,16 +1949,21 @@ static int rmp4_video_stream_advance(rmp4_video_stream_t *s,
s->completed = 0;
s->waiting = 0;
#ifdef HAVE_RAV1
if (s->codec == RMP4_CODEC_AV1)
bounded = s->codec == RMP4_CODEC_AV1;
#endif
#ifdef HAVE_RVP9
bounded |= s->vp9_hw != NULL;
#endif
#if defined(HAVE_RAV1) || defined(HAVE_RVP9)
if (bounded)
started = cpu_features_get_time_usec();
#endif
/* Retry later for input or decoder capacity, without spinning. */
while ((r = rmp4_video_stream_step(s, duration_ms, discard)) == 0)
{
#ifdef HAVE_RAV1
if (s->codec == RMP4_CODEC_AV1 &&
cpu_features_get_time_usec() - started >= 4000)
#if defined(HAVE_RAV1) || defined(HAVE_RVP9)
if (bounded && cpu_features_get_time_usec() - started >= 4000)
{
s->waiting = RMP4_VIDEO_WAIT_DECODER;
return 2;
@@ -1774,6 +2018,18 @@ int rmp4_video_stream_set_hw(rmp4_video_stream_t *s, int enable)
s->want_hw = 0;
return s->av1 != NULL;
}
#endif
#ifdef HAVE_RVP9
if (s && s->codec == RMP4_CODEC_VP9)
{
if (!enable || s->vp9_hw)
return s->vp9_hw != NULL;
s->want_hw = 1;
rmp4_video_stream_rewind(s);
if (!s->vp9_hw)
s->want_hw = 0;
return s->vp9_hw != NULL;
}
#endif
if (!s || (s->codec != RMP4_CODEC_H264 && s->codec != RMP4_CODEC_H265))
return 0;
@@ -2311,6 +2567,16 @@ void rmp4_video_stream_rewind(rmp4_video_stream_t *s)
rmp4_rewind(s->demux);
s->disp_idx = 0;
s->wait_key = 0;
#ifdef HAVE_RVP9
if (s->vp9_hw && s->want_hw && s->vp9_backend.restart)
{
rvp9_hw_session_restart(s->vp9_hw);
s->vp9_restart = 1;
s->vp9_active = 0;
s->vp9_sample = 0;
return;
}
#endif
/* The stream restarts at a key frame, so a fresh decoder (empty
* reference chain, default probabilities) is the correct state. */
#ifdef HAVE_RAV1
@@ -2668,6 +2934,9 @@ int rmp4_video_stream_seek_step(rmp4_video_stream_t *s, int64_t *position_us)
s->av1_decode_sample = (uint32_t)sample;
s->av1->hdr = *rmp4_video_av1_hdr_at(s, (uint32_t)sample);
}
#endif
#ifdef HAVE_RVP9
s->vp9_sample = (uint32_t)sample;
#endif
if (!rmp4_select_track_sample(s->demux, s->track,
(uint32_t)sample))
+329 -21
View File
@@ -175,6 +175,10 @@ typedef struct rrar_unpack
{
rrar_bits_t br;
uint8_t *out;
/* pos, size and watch_at are counted from out[0], which is the
* member's octet base: 0 when the member is unpacked whole, further
* on each time a member streamed out round a ring comes round to its
* start (rrar_archive_extract_sink()) */
uint64_t pos;
uint64_t size;
@@ -215,6 +219,18 @@ typedef struct rrar_unpack
uint64_t window;
uint32_t next_filter;
uint8_t *work; /* the filters' second copy */
/* Streamed out: where out[0] is in the member, how much of it has
* been handed over, and to whom; out is a ring, and ext where it
* ended the last time round, 0 before it has come round */
uint64_t base;
uint64_t flushed;
size_t cap; /* the size of out */
size_t ext;
int (*sink)(void *ud, const uint8_t *data, size_t len);
void *sink_ud;
uint8_t *fbuf; /* a filter's block, filtered */
uint32_t crc; /* of what has been handed over */
} rrar_unpack_t;
struct rrar_archive
@@ -645,7 +661,7 @@ static int filter_parse(rrar_unpack_t *u, const uint8_t *bytes, uint32_t length,
prog->usage++;
}
block_start = (uint64_t)vm_number(&br) + u->pos;
block_start = (uint64_t)vm_number(&br) + u->base + u->pos;
if (flags & 0x40)
block_start += 258;
if (flags & 0x20)
@@ -713,9 +729,9 @@ static int filter_parse(rrar_unpack_t *u, const uint8_t *bytes, uint32_t length,
return RRAR_ERROR_DATA;
if ( block_length > VM_MEMORY_SIZE
|| block_start < u->pos
|| block_start > u->size
|| block_length > u->size - block_start)
|| block_start < u->base + u->pos
|| block_start > u->base + u->size
|| block_length > u->base + u->size - block_start)
return RRAR_ERROR_DATA;
if (u->num_filters == u->cap_filters)
@@ -969,10 +985,10 @@ static void filter5_arm(uint8_t *mem, uint32_t length, uint64_t start)
/* The member is all unpacked: its filters, in the order they came. Two
* for the same block are one after the other on it. */
/* One filter over its block, which is all there. */
static int filter_apply(rrar_unpack_t *u, const rrar_filter_t *f)
/* One filter over its block, which is all there, at @block. */
static int filter_apply(rrar_unpack_t *u, const rrar_filter_t *f,
uint8_t *block)
{
uint8_t *block = u->out + (size_t)f->start;
uint32_t length = f->length;
if (f->kind >= FILTER5_DELTA)
@@ -1045,7 +1061,8 @@ static int filters_run(rrar_unpack_t *u)
int r = RRAR_OK;
for (; u->next_filter < u->num_filters && r == RRAR_OK; u->next_filter++)
r = filter_apply(u, &u->filters[u->next_filter]);
r = filter_apply(u, &u->filters[u->next_filter],
u->out + (size_t)u->filters[u->next_filter].start);
return r;
}
@@ -1060,12 +1077,17 @@ static int filters_run(rrar_unpack_t *u)
* the bytes as they were before any filter. In the order they were read,
* as at the end. */
#define RRAR_WATCH_STEP 0x10000
static int unpack_flush(rrar_unpack_t *u);
static int unpack_watch(rrar_unpack_t *u)
{
uint64_t done = u->pos;
uint32_t n;
int r;
if (u->sink)
return unpack_flush(u);
if (!u->watch)
{
u->watch_at = u->size;
@@ -1077,7 +1099,7 @@ static int unpack_watch(rrar_unpack_t *u)
if (u->pos < f->start + f->length + u->window)
break;
if ((r = filter_apply(u, f)) != RRAR_OK)
if ((r = filter_apply(u, f, u->out + (size_t)f->start)) != RRAR_OK)
return r;
u->next_filter++;
}
@@ -1092,6 +1114,116 @@ static int unpack_watch(rrar_unpack_t *u)
return RRAR_OK;
}
/* A member streamed out: what of it is final goes to the sink in order -
* up to the next filter's block, which goes once it is all there and
* filtered in a copy, the ring keeping the octets as they were for the
* matches still to come. The ring is the window, a filter's block and
* two steps: once the next step would not fit before its end, the
* decoder goes on from its start, and a match reaching back before the
* start is copied from the end and on from the start. */
#define RRAR_SINK_SLACK 0x10000
/* how often what is final is handed over; a test makes it smaller, and
* with RRAR_SINK_ALWAYS streams even a member that would fit whole */
#ifndef RRAR_SINK_STEP
#define RRAR_SINK_STEP 0x100000
#endif
/* @len octets of the member from @at, which may be in the end of the
* ring before its start: the part before, then the part after */
static const uint8_t *unpack_ring(const rrar_unpack_t *u, uint64_t at,
size_t *len)
{
if (at >= u->base)
return u->out + (size_t)(at - u->base);
if (*len > u->base - at)
*len = (size_t)(u->base - at);
return u->out + u->ext - (size_t)(u->base - at);
}
static int unpack_emit(rrar_unpack_t *u, const uint8_t *data, size_t len)
{
if (!len)
return RRAR_OK;
u->crc = encoding_crc32(u->crc, data, len);
u->flushed += len;
return u->sink(u->sink_ud, data, len) ? RRAR_ERROR_CANCELLED : RRAR_OK;
}
static int unpack_flush(rrar_unpack_t *u)
{
uint64_t at = u->base + u->pos;
int r;
for (;;)
{
const rrar_filter_t *f = NULL;
uint64_t to = at;
if (u->next_filter < u->num_filters)
{
f = &u->filters[u->next_filter];
/* (one that reaches back into what has gone is not for here) */
if (f->start < u->flushed)
return RRAR_ERROR_UNSUPPORTED;
if (f->start < to)
to = f->start;
}
while (u->flushed < to)
{
size_t n = (size_t)(to - u->flushed);
const uint8_t *d = unpack_ring(u, u->flushed, &n);
if ((r = unpack_emit(u, d, n)) != RRAR_OK)
return r;
}
if (u->next_filter >= u->num_filters || u->flushed < f->start
|| at < f->start + f->length)
break;
/* the block is all there: filtered in a copy, with those after it
* for the same block */
if (!u->fbuf && !(u->fbuf = (uint8_t *)malloc(FILTER5_BLOCK_MAX)))
return RRAR_ERROR_MEM;
if (f->length > FILTER5_BLOCK_MAX)
return RRAR_ERROR_DATA;
{
size_t got = 0;
while (got < f->length)
{
size_t n = f->length - got;
const uint8_t *d = unpack_ring(u, f->start + got, &n);
memcpy(u->fbuf + got, d, n);
got += n;
}
}
do
{
if ((r = filter_apply(u, &u->filters[u->next_filter], u->fbuf))
!= RRAR_OK)
return r;
u->next_filter++;
} while ( u->next_filter < u->num_filters
&& u->filters[u->next_filter].start == f->start
&& u->filters[u->next_filter].length == f->length);
if ((r = unpack_emit(u, u->fbuf, f->length)) != RRAR_OK)
return r;
}
/* round to the start, once the next step would not fit; what is not
* handed over yet, and the window, stay in the end until the decoder
* gets that far again, which the ring is sized for */
if (u->pos + RRAR_SINK_STEP + RRAR_SINK_SLACK > u->cap)
{
u->ext = (size_t)u->pos;
u->base += u->pos;
u->size -= u->pos;
u->pos = 0;
}
u->watch_at = u->pos + RRAR_SINK_STEP;
/* a filter's block waiting longer than the ring can keep */
if ( u->flushed < u->base
&& u->ext - (size_t)(u->base - u->flushed)
< u->watch_at + RRAR_SINK_SLACK)
return RRAR_ERROR_UNSUPPORTED;
return RRAR_OK;
}
/* ------------------------------------------------------------ the member */
static INLINE void emit_literal(rrar_unpack_t *u, uint8_t byte)
@@ -1099,14 +1231,28 @@ static INLINE void emit_literal(rrar_unpack_t *u, uint8_t byte)
u->out[(size_t)u->pos++] = byte;
}
/* A match that reaches back before the member's start, or to no
* distance at all: what is not there is zeros. */
static size_t copy_match_edge(uint8_t *out, size_t pos, uint32_t offset, uint32_t length)
/* A match that reaches back before out[0]: from the end of the ring
* where it has come round (@ext, 0 when not), and on from out[0]; or
* else before the member's start, or to no distance at all, where what
* is not there is zeros. */
static size_t copy_match_edge(uint8_t *out, size_t pos, uint32_t offset,
uint32_t length, size_t ext)
{
uint8_t *dst = out + pos;
if (offset == 0)
memset(dst, 0, length);
else if (ext && offset - pos <= ext)
{
size_t back = offset - pos;
uint32_t k = back < length ? (uint32_t)back : length;
uint32_t i;
memmove(dst, out + ext - back, k);
/* (from out[0] on, pos + k behind) */
for (i = k; i < length; i++)
dst[i] = out[i - k];
}
else
{
size_t before = offset - pos;
@@ -1124,7 +1270,7 @@ static size_t copy_match_edge(uint8_t *out, size_t pos, uint32_t offset, uint32_
/* @length bytes from @offset back, to @out at @pos. Past the member's end
* they are not written. The position after them. */
static INLINE size_t copy_match(uint8_t *out, size_t pos, size_t size,
uint32_t offset, uint32_t length)
uint32_t offset, uint32_t length, size_t ext)
{
uint8_t *dst;
const uint8_t *src;
@@ -1133,7 +1279,7 @@ static INLINE size_t copy_match(uint8_t *out, size_t pos, size_t size,
if (length > size - pos)
length = (uint32_t)(size - pos);
if (offset == 0 || offset > pos)
return copy_match_edge(out, pos, offset, length);
return copy_match_edge(out, pos, offset, length, ext);
next = pos + length;
dst = out + pos;
@@ -1160,7 +1306,8 @@ static INLINE size_t copy_match(uint8_t *out, size_t pos, size_t size,
static void emit_match(rrar_unpack_t *u, uint32_t offset, uint32_t length)
{
u->pos = copy_match(u->out, (size_t)u->pos, (size_t)u->size, offset, length);
u->pos = copy_match(u->out, (size_t)u->pos, (size_t)u->size, offset,
length, u->ext);
}
/* What an LZ block's symbols stand for. */
@@ -1211,6 +1358,7 @@ static int unpack_lz(rrar_unpack_t *u)
size_t size = (size_t)u->size;
/* (to the member's end, or to where the watcher is next told) */
size_t stop = u->watch_at < u->size ? (size_t)u->watch_at : size;
size_t ext = u->ext;
int r = RRAR_OK;
while (pos < stop)
@@ -1356,7 +1504,7 @@ static int unpack_lz(rrar_unpack_t *u)
}
u->last_offset = offs;
u->last_length = len;
pos = copy_match(out, pos, size, offs, len);
pos = copy_match(out, pos, size, offs, len, ext);
}
u->br = br;
@@ -1562,7 +1710,7 @@ static int unpack5_filter(rrar_unpack_t *u, rrar_bits_t *br, size_t pos)
uint32_t length = filter5_number(br);
unsigned type = rrar_br_get(br, 3);
uint32_t channels = type == 0 ? rrar_br_get(br, 5) + 1 : 0;
uint64_t block = (uint64_t)pos + start;
uint64_t block = u->base + (uint64_t)pos + start;
rrar_filter_t *f;
if (rrar_br_overrun(br) || length < 4 || length > FILTER5_BLOCK_MAX)
@@ -1570,7 +1718,7 @@ static int unpack5_filter(rrar_unpack_t *u, rrar_bits_t *br, size_t pos)
if (type > 3)
return RRAR_ERROR_UNSUPPORTED;
/* within the member, and after the last filter's block */
if (block > u->size || length > u->size - block)
if (block > u->base + u->size || length > u->base + u->size - block)
return RRAR_ERROR_DATA;
if (u->num_filters)
{
@@ -1626,6 +1774,8 @@ static int unpack50(rrar_unpack_t *u)
u->pos = pos;
if ((r = unpack_watch(u)) != RRAR_OK)
return r;
pos = (size_t)u->pos;
size = (size_t)u->size;
}
/* the block's header: flags, a checksum, and its size */
if (end - p < 3)
@@ -1661,7 +1811,12 @@ static int unpack50(rrar_unpack_t *u)
else if (!have_tables)
return RRAR_ERROR_DATA;
while (pos < size)
for (;;)
{
/* (to where the watcher is next told: a block can be long, and a
* member streamed out has only so much room) */
size_t stop = u->watch_at < size ? (size_t)u->watch_at : size;
while (pos < stop)
{
int symbol;
uint32_t len;
@@ -1768,7 +1923,15 @@ static int unpack50(rrar_unpack_t *u)
}
if (rrar_br_overrun(&br))
return RRAR_ERROR_DATA;
pos = copy_match(out, pos, size, (uint32_t)dist, len);
pos = copy_match(out, pos, size, (uint32_t)dist, len, u->ext);
}
if (block_done || pos >= size)
break;
u->pos = pos;
if ((r = unpack_watch(u)) != RRAR_OK)
return r;
pos = (size_t)u->pos;
size = (size_t)u->size;
}
if (rrar_br_overrun(&br))
@@ -1816,6 +1979,67 @@ static int unpack_member(const uint8_t *packed, size_t packed_len,
return r;
}
/* The ring a member is streamed out through: its window or a filter's
* largest block and a step past it, whichever is more, and two steps
* more for what is decoded after the start comes round */
static uint64_t unpack_sink_cap(uint64_t window)
{
uint64_t keep = window;
if (keep < (uint64_t)FILTER5_BLOCK_MAX + RRAR_SINK_STEP)
keep = (uint64_t)FILTER5_BLOCK_MAX + RRAR_SINK_STEP;
return keep + 2 * RRAR_SINK_STEP + 2 * RRAR_SINK_SLACK;
}
/* A member of @size unpacked through a buffer of unpack_sink_cap(),
* handed to @sink in order; *@crc receives the CRC of it all. */
static int unpack_member_sink(const uint8_t *packed, size_t packed_len,
uint64_t size, int rar5, uint64_t window,
int (*sink)(void *ud, const uint8_t *data, size_t len), void *ud,
uint32_t *crc)
{
rrar_unpack_t *u = (rrar_unpack_t *)calloc(1, sizeof(*u));
uint64_t cap = unpack_sink_cap(window);
int r;
if (!u)
return RRAR_ERROR_MEM;
if (cap > (uint64_t)((size_t)-1) || !(u->out = (uint8_t *)malloc((size_t)cap)))
{
free(u);
return RRAR_ERROR_MEM;
}
u->br.p = packed;
u->br.end = packed + packed_len;
u->cap = (size_t)cap;
u->size = size;
u->ppmd_escape = 2;
u->watch_at = RRAR_SINK_STEP;
u->window = window;
u->sink = sink;
u->sink_ud = ud;
rrar_ppmd7_construct(&u->ppmd);
r = rar5 ? unpack50(u) : unpack29(u);
if (r == RRAR_OK && u->pos != u->size)
r = RRAR_ERROR_DATA;
/* the rest, and the filters' blocks still to go */
if (r == RRAR_OK)
r = unpack_flush(u);
if ( r == RRAR_OK && (u->flushed != u->base + u->size
|| u->next_filter != u->num_filters))
r = RRAR_ERROR_DATA;
*crc = u->crc;
rrar_ppmd7_free(&u->ppmd);
free(u->programs);
free(u->filters);
free(u->work);
free(u->fbuf);
free(u->out);
free(u);
return r;
}
/* -------------------------------------------------------------- headers */
/* A name with the Unicode flag: the name in the system's code page, a
@@ -2537,7 +2761,8 @@ int rrar_archive_extract(rrar_archive_t *a, uint32_t index,
return RRAR_ERROR_UNSUPPORTED;
size = (size_t)e->size;
if (!(buf = (uint8_t *)malloc(size ? size : 1)))
/* an octet to spare, for a NUL */
if (size == (size_t)-1 || !(buf = (uint8_t *)malloc(size + 1)))
return RRAR_ERROR_MEM;
if ((r = rrar_extract_into(a, e, buf, NULL)) != RRAR_OK)
{
@@ -2549,6 +2774,89 @@ int rrar_archive_extract(rrar_archive_t *a, uint32_t index,
return RRAR_OK;
}
int rrar_archive_extract_sink(rrar_archive_t *a, uint32_t index,
int (*sink)(void *ud, const uint8_t *data, size_t len), void *ud)
{
const rrar_entry_t *e;
uint32_t crc = 0;
int r = RRAR_OK;
if (!a || !sink || index >= a->num_entries)
return RRAR_ERROR_PARAM;
e = &a->entries[index];
if (!e->supported)
return RRAR_ERROR_UNSUPPORTED;
if (e->method == RAR_METHOD_STORED)
{
/* where it lies, or read a piece at a time */
uint64_t at = 0;
uint8_t *buf = a->data ? NULL : (uint8_t *)malloc(RRAR_SINK_STEP);
if (!a->data && !buf)
return RRAR_ERROR_MEM;
while (r == RRAR_OK && at < e->size)
{
size_t n = e->size - at < RRAR_SINK_STEP
? (size_t)(e->size - at) : RRAR_SINK_STEP;
const uint8_t *d = buf;
if (a->data)
d = a->data + (size_t)(e->data_offset + at);
else
r = rrar_fetch(a, e->data_offset + at, buf, n);
if (r == RRAR_OK)
{
crc = encoding_crc32(crc, d, n);
if (sink(ud, d, n))
r = RRAR_ERROR_CANCELLED;
}
at += n;
}
free(buf);
}
#ifndef RRAR_SINK_ALWAYS
else if (e->size <= unpack_sink_cap(e->window))
{
/* no bigger than the buffer it would go through: whole */
uint8_t *buf = NULL;
size_t len = 0;
if ((r = rrar_archive_extract(a, index, &buf, &len)) == RRAR_OK
&& sink(ud, buf, len))
r = RRAR_ERROR_CANCELLED;
free(buf);
return r;
}
#endif
else
{
const uint8_t *packed = a->data
? a->data + (size_t)e->data_offset : NULL;
uint8_t *in = NULL;
if (!packed)
{
/* An archive that is read: the member's packed bytes are read
* in for as long as it takes to decode them. */
if ( e->packed_size > (uint64_t)((size_t)-1)
|| !(in = (uint8_t *)malloc((size_t)e->packed_size
? (size_t)e->packed_size : 1)))
return RRAR_ERROR_MEM;
if ((r = rrar_fetch(a, e->data_offset, in, (size_t)e->packed_size))
!= RRAR_OK)
{
free(in);
return r;
}
packed = in;
}
r = unpack_member_sink(packed, (size_t)e->packed_size, e->size,
e->version == 50, e->window, sink, ud, &crc);
free(in);
}
if (r == RRAR_OK && e->has_crc && crc != e->crc)
r = RRAR_ERROR_CRC;
return r;
}
int rrar_archive_extract_to(rrar_archive_t *a, uint32_t index,
uint8_t *dst, size_t dst_size, const rrar_watch_t *watch)
{
+43 -6
View File
@@ -146,6 +146,16 @@ static INLINE uint32_t ppmd_rc_byte(rrar_ppmd7_range_t *rc)
static INLINE void ppmd_rc_normalize(rrar_ppmd7_range_t *rc)
{
if (rc->sevenz)
{
while (rc->range < PPMD_RC_TOP)
{
rc->code = (rc->code << 8) | ppmd_rc_byte(rc);
rc->range <<= 8;
rc->low <<= 8;
}
return;
}
for (;;)
{
if ((rc->low ^ (rc->low + rc->range)) >= PPMD_RC_TOP)
@@ -180,6 +190,21 @@ static INLINE void ppmd_rc_decode(rrar_ppmd7_range_t *rc,
* size0 * range exactly when the quotient is under size0. */
static INLINE unsigned ppmd_rc_bit(rrar_ppmd7_range_t *rc, uint32_t size0)
{
if (rc->sevenz)
{
/* 7z's gives the 1 side what the scale does not divide */
uint32_t bound = (rc->range >> (PPMD_INT_BITS + PPMD_PERIOD_BITS)) * size0;
if (rc->code - rc->low < bound)
{
rc->range = bound;
ppmd_rc_normalize(rc);
return 0;
}
rc->low += bound;
rc->range -= bound;
ppmd_rc_normalize(rc);
return 1;
}
rc->range >>= PPMD_INT_BITS + PPMD_PERIOD_BITS;
if (rc->code - rc->low < size0 * rc->range)
{
@@ -1031,13 +1056,25 @@ bool rrar_ppmd7_range_init(rrar_ppmd7_range_t *rc, const uint8_t *data,
const uint8_t *end, uint32_t past)
{
unsigned i;
rc->p = data;
rc->end = end;
rc->past = past;
rc->low = 0;
rc->range = 0xFFFFFFFFu;
rc->code = 0;
rc->p = data;
rc->end = end;
rc->past = past;
rc->low = 0;
rc->range = 0xFFFFFFFFu;
rc->code = 0;
rc->sevenz = 0;
for (i = 0; i < 4; i++)
rc->code = (rc->code << 8) | ppmd_rc_byte(rc);
return rc->code < 0xFFFFFFFFu;
}
bool rrar_ppmd7z_range_init(rrar_ppmd7_range_t *rc, const uint8_t *data,
const uint8_t *end)
{
if (data >= end || *data)
return false;
if (!rrar_ppmd7_range_init(rc, data + 1, end, 0))
return false;
rc->sevenz = 1;
return true;
}
+12 -2
View File
@@ -20,7 +20,8 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* The PPMd variant H model RAR 2.9 packs with, and RAR's range decoder.
/* The PPMd variant H model RAR 2.9 packs with, and RAR's range decoder
* and 7z's.
*
* PPMd var.H is Dmitry Shkarin's prediction by partial matching (public
* domain, 2001); RAR pairs it with a carry-less range coder of its own.
@@ -92,7 +93,10 @@ typedef struct rrar_ppmd7
uint8_t hb2flag[256];
} rrar_ppmd7_t;
/* RAR's range decoder, over a span of the packed data. */
/* The range decoder, RAR's or 7z's, over a span of the packed data.
* 7z's renormalises on the range alone, without RAR's carry-less step,
* and a binary decision leaves the 1 side what the scale does not
* divide. */
typedef struct rrar_ppmd7_range
{
const uint8_t *p;
@@ -101,6 +105,7 @@ typedef struct rrar_ppmd7_range
uint32_t code;
uint32_t low;
uint32_t past; /* bytes read past the end, as zeros */
int sevenz; /* 7z's coder */
} rrar_ppmd7_range_t;
/* Once, before anything else. */
@@ -120,6 +125,11 @@ void rrar_ppmd7_init(rrar_ppmd7_t *p, unsigned max_order);
bool rrar_ppmd7_range_init(rrar_ppmd7_range_t *rc, const uint8_t *data,
const uint8_t *end, uint32_t past);
/* 7z's decoder at @data, a PPMd coder's whole stream: a zero octet,
* then the code. false if the bytes cannot start one. */
bool rrar_ppmd7z_range_init(rrar_ppmd7_range_t *rc, const uint8_t *data,
const uint8_t *end);
/* The next byte, or a negative number if the data is not PPMd's. */
int rrar_ppmd7_decode_symbol(rrar_ppmd7_t *p, rrar_ppmd7_range_t *rc);
+1386
View File
File diff suppressed because it is too large. Load diff
+4 -2
View File
@@ -890,8 +890,8 @@ static enum video_player_state vp_poll_threaded(video_player_t *p,
}
if ((su == VP_JOB_FINISHED || su == VP_JOB_COMPLETED)
&& (p->flags & VP_HW) && !p->presented)
return video_player_request_software(p)
&& !p->presented)
return (p->flags & VP_HW) && video_player_request_software(p)
? VIDEO_PLAYER_PLAYING : VIDEO_PLAYER_ENDED;
if (p->movie_us && (su == VP_JOB_READY || su == VP_JOB_FINISHED
|| su == VP_JOB_COMPLETED))
@@ -1121,6 +1121,8 @@ finished:
&& image_transfer_anim_stream_completed(p->stream, p->type);
if ((p->flags & VP_HW) && !p->presented && video_player_request_software(p))
return VIDEO_PLAYER_PLAYING;
if (!p->presented)
return VIDEO_PLAYER_ENDED;
if (p->movie_us)
{
p->inline_finished = true;
+7 -10
View File
@@ -2490,6 +2490,9 @@ static INLINE void rvq_neon_pair2(int32_t *e0, int32_t *e2,
RVQ_WRAPS static void imdct_step3_iter0_loop_q(int n, int32_t *e, int i_off, int k_off, int32_t *A)
{
int32_t *ee0 = e + i_off;
int32_t *ee2 = ee0 + k_off;
int i;
#if RVQ_HAVE_NEON_MDCT
{
int32_t *ee0 = e + i_off;
@@ -2508,10 +2511,6 @@ RVQ_WRAPS static void imdct_step3_iter0_loop_q(int n, int32_t *e, int i_off, int
}
#endif
int32_t *ee0 = e + i_off;
int32_t *ee2 = ee0 + k_off;
int i;
assert((n & 3) == 0);
for (i=(n>>2); i > 0; --i)
{
@@ -2553,6 +2552,10 @@ RVQ_WRAPS static void imdct_step3_iter0_loop_q(int n, int32_t *e, int i_off, int
RVQ_WRAPS static void imdct_step3_inner_r_loop_q(int lim, int32_t *e, int d0, int k_off, int32_t *A, int k1)
{
int i;
int32_t k00_20, k01_21;
int32_t *e0 = e + d0;
int32_t *e2 = e0 + k_off;
#if RVQ_HAVE_NEON_MDCT
{
int i;
@@ -2570,12 +2573,6 @@ RVQ_WRAPS static void imdct_step3_inner_r_loop_q(int lim, int32_t *e, int d0, in
}
#endif
int i;
int32_t k00_20, k01_21;
int32_t *e0 = e + d0;
int32_t *e2 = e0 + k_off;
for (i=lim >> 2; i > 0; --i) {
k00_20 = e0[-0] - e2[-0];
k01_21 = e0[-1] - e2[-1];
+24 -1
View File
@@ -106,7 +106,7 @@ struct rwebm_video_stream
rvp9_hw_session_t *hw;
rvp9_hw_backend_t hw_backend;
size_t super_sizes[RWEBM_VIDEO_MAX_SUPER], super_offset;
int super_count, super_index, last_show, submitted;
int super_count, super_index, last_show, submitted, hw_restart;
#endif
#ifdef HAVE_RAV1
rav1_hw_stream_t *av1;
@@ -709,6 +709,7 @@ static void rwebm_video_stream_close_decoder(rwebm_video_stream_t *s)
}
#endif
#ifdef HAVE_RVP9
s->hw_restart = 0;
if (s->hw)
{
rvp9_hw_session_cancel(s->hw);
@@ -2080,6 +2081,16 @@ static int rwebm_video_hw_step(
{
int ret, duration;
s->waiting = 0;
if (s->hw_restart)
{
ret = s->hw_backend.restart(s->hw->ops.decoder);
if (ret)
{
s->waiting = ret > 0;
return ret > 0 ? 2 : -1;
}
s->hw_restart = 0;
}
if (s->pending && s->hw_skip != discard)
return -1;
if (!s->pending)
@@ -2969,6 +2980,18 @@ void rwebm_video_stream_rewind(rwebm_video_stream_t *s)
s->pkt_idx = 0;
s->have_frame_time = 0;
s->have_decode_time = 0;
#ifdef HAVE_RVP9
if (s->hw && s->hw_want && s->hw_backend.restart)
{
if (s->hw_untaken)
video_hw_frame_abandon(&s->hw_frame);
s->hw_untaken = s->pending = s->waiting = 0;
memset(&s->hw_frame, 0, sizeof(s->hw_frame));
rvp9_hw_session_restart(s->hw);
s->hw_restart = 1;
return;
}
#endif
/* The stream restarts at a key frame, so a fresh decoder (empty
* reference chain, default probabilities) is the correct state. */
rwebm_video_stream_close_decoder(s);
+276 -1
View File
@@ -20,6 +20,10 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* retro_environment.h spells MSVC's SSE/SSE2 targets as __SSE__/__SSE2__,
* which the SIMD paths below test. */
#include <retro_environment.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
@@ -158,6 +162,259 @@ void conv_0rgb1555_rgb565(void *output_, const void *input_,
}
}
/* AVX2 and AVX-512BW forms of the 16-bit -> 32-bit converters the video
* drivers run every frame (RGB565 -> ARGB/ABGR, 0RGB1555 -> ARGB). They
* are built per function, whatever the build's baseline, and chosen at
* run time, so older CPUs keep the SSE2 path and older compilers simply
* leave them out. Only RetroArch's own build (RARCH_INTERNAL), or one
* that asks with PIXCONV_RUNTIME_SIMD, compiles them: the choice reads
* cpu_features_get(), and cores that carry pixconv.c need not link
* features_cpu.c. */
#if !defined(SCALER_NO_SIMD) && defined(__SSE2__) \
&& (defined(RARCH_INTERNAL) || defined(PIXCONV_RUNTIME_SIMD)) \
&& (defined(__x86_64__) || defined(__i386__) \
|| defined(_M_X64) || defined(_M_IX86))
#if defined(_MSC_VER) && !defined(__clang__)
#if _MSC_VER >= 1911
#define PIXCONV_HAVE_WIDE 1
#define PIXCONV_HAVE_AVX512 1
#define PIXCONV_TARGET_AVX2
#define PIXCONV_TARGET_AVX512
#endif
#elif defined(__clang__) && (defined(__SCE__) || defined(_MSC_VER))
/* Clang's immintrin.h for PlayStation and clang-cl only declares the
* intrinsics of ISAs enabled for the whole build, so per-function
* targets cannot reach them; build a tier only when the baseline has
* it (PS5's Zen 2 keeps AVX2, PS4's Jaguar has neither). */
#ifdef __AVX2__
#define PIXCONV_HAVE_WIDE 1
#define PIXCONV_TARGET_AVX2
#if defined(__AVX512F__) && defined(__AVX512BW__)
#define PIXCONV_HAVE_AVX512 1
#define PIXCONV_TARGET_AVX512
#endif
#endif
#elif (defined(__clang__) && __clang_major__ >= 4) \
|| (defined(__GNUC__) && !defined(__clang__) && __GNUC__ >= 5)
#define PIXCONV_HAVE_WIDE 1
#define PIXCONV_HAVE_AVX512 1
#define PIXCONV_TARGET_AVX2 __attribute__((target("avx2")))
#define PIXCONV_TARGET_AVX512 __attribute__((target("avx512f,avx512bw")))
#endif
#endif
#ifdef PIXCONV_HAVE_WIDE
#include <immintrin.h>
#include <retro_atomic.h>
#include <features/features_cpu.h>
enum pixconv_16to32
{
PIXCONV_565_ARGB = 0,
PIXCONV_565_ABGR,
PIXCONV_1555_ARGB
};
/* MSVC leaves the upper vector halves dirty on return; GCC and Clang
* clear them themselves for target functions. */
#if defined(_MSC_VER) && !defined(__clang__)
#define PIXCONV_ZEROUPPER() _mm256_zeroupper()
#else
#define PIXCONV_ZEROUPPER()
#endif
/* 0 = SSE2 path, 1 = AVX2, 2 = AVX-512BW. Several threads convert
* (video, recording, menu), so the one-time answer is published with
* an atomic store; racing first calls compute the same value. */
static retro_atomic_int_t pixconv_tier_cached;
#ifdef PIXCONV_TEST_HOOKS
/* -1 picks by CPU; otherwise caps the tier for a test. */
int pixconv_tier_cap = -1;
#endif
static int pixconv_simd_tier(void)
{
int tier = retro_atomic_load_acquire_int(&pixconv_tier_cached);
if (tier & 4)
tier &= 3;
else
{
uint64_t cpu = cpu_features_get();
tier = 0;
if (cpu & RETRO_SIMD_AVX2)
tier = 1;
#ifdef PIXCONV_HAVE_AVX512
if (cpu & RETRO_SIMD_AVX512)
tier = 2;
#endif
retro_atomic_store_release_int(&pixconv_tier_cached, 4 | tier);
}
#ifdef PIXCONV_TEST_HOOKS
if (pixconv_tier_cap >= 0 && tier > pixconv_tier_cap)
tier = pixconv_tier_cap;
#endif
return tier;
}
static INLINE uint32_t pixconv_16to32_px(uint16_t c, int fmt)
{
switch (fmt)
{
case PIXCONV_565_ABGR:
return 0xff000000u | pixconv_rgb565_to_xbgr8888(c);
case PIXCONV_1555_ARGB:
return 0xff000000u | pixconv_0rgb1555_to_xrgb8888(c);
default:
break;
}
return 0xff000000u | pixconv_rgb565_to_xrgb8888(c);
}
/* The SSE2 arithmetic at a wider width: each channel masked to the top
* of a 16-bit lane and multiplied so the high half holds it expanded to
* 8 bits, then the bytes interleaved into pixels. 565 and 1555 differ
* only in red's shift and green's mask and multiplier; ARGB and ABGR
* only in which of red and blue pairs with green. */
#define PIXCONV_16TO32_CONSTS(fmt) \
int r_shift = ((fmt) == PIXCONV_1555_ARGB) ? 0 : 1; \
int16_t g_mask = ((fmt) == PIXCONV_1555_ARGB) \
? (int16_t)(0x1f << 5) : (int16_t)(0x3f << 5); \
int16_t g_mul = ((fmt) == PIXCONV_1555_ARGB) \
? (int16_t)0x4200 : (int16_t)0x2080; \
int swap_rb = ((fmt) == PIXCONV_565_ABGR)
PIXCONV_TARGET_AVX2
static void pixconv_16to32_avx2(uint32_t *output, const uint16_t *input,
int width, int height, int out_stride, int in_stride, int fmt)
{
int h;
PIXCONV_16TO32_CONSTS(fmt);
const __m128i rs = _mm_cvtsi32_si128(r_shift);
const __m256i mask_r = _mm256_set1_epi16(0x1f << 10);
const __m256i mask_g = _mm256_set1_epi16(g_mask);
const __m256i mask_b = _mm256_set1_epi16(0x1f << 5);
const __m256i mul_r = _mm256_set1_epi16(0x0210);
const __m256i mul_g = _mm256_set1_epi16(g_mul);
const __m256i mul_b = _mm256_set1_epi16(0x4200);
const __m256i a = _mm256_set1_epi16(0x00ff);
for (h = 0; h < height;
h++, output += out_stride >> 2, input += in_stride >> 1)
{
int w = 0;
for (; w + 16 <= width; w += 16)
{
__m256i lo, hi, c0, c2;
const __m256i in = _mm256_loadu_si256((const __m256i*)(input + w));
__m256i r = _mm256_mulhi_epi16(_mm256_and_si256(
_mm256_srl_epi16(in, rs), mask_r), mul_r);
__m256i g = _mm256_mulhi_epi16(_mm256_and_si256(in, mask_g), mul_g);
__m256i b = _mm256_mulhi_epi16(_mm256_and_si256(
_mm256_slli_epi16(in, 5), mask_b), mul_b);
c0 = swap_rb ? r : b;
c2 = swap_rb ? b : r;
/* unpack works per 128-bit lane: lo holds pixels 0-3 and 8-11,
* hi 4-7 and 12-15; the permutes put them back in order. */
lo = _mm256_or_si256(_mm256_unpacklo_epi8(c0, g),
_mm256_slli_si256(_mm256_unpacklo_epi8(c2, a), 2));
hi = _mm256_or_si256(_mm256_unpackhi_epi8(c0, g),
_mm256_slli_si256(_mm256_unpackhi_epi8(c2, a), 2));
_mm256_storeu_si256((__m256i*)(output + w),
_mm256_permute2x128_si256(lo, hi, 0x20));
_mm256_storeu_si256((__m256i*)(output + w + 8),
_mm256_permute2x128_si256(lo, hi, 0x31));
}
for (; w < width; w++)
output[w] = pixconv_16to32_px(input[w], fmt);
}
PIXCONV_ZEROUPPER();
}
#ifdef PIXCONV_HAVE_AVX512
PIXCONV_TARGET_AVX512
static void pixconv_16to32_avx512(uint32_t *output, const uint16_t *input,
int width, int height, int out_stride, int in_stride, int fmt)
{
int h;
PIXCONV_16TO32_CONSTS(fmt);
const __m128i rs = _mm_cvtsi32_si128(r_shift);
const __m512i mask_r = _mm512_set1_epi16(0x1f << 10);
const __m512i mask_g = _mm512_set1_epi16(g_mask);
const __m512i mask_b = _mm512_set1_epi16(0x1f << 5);
const __m512i mul_r = _mm512_set1_epi16(0x0210);
const __m512i mul_g = _mm512_set1_epi16(g_mul);
const __m512i mul_b = _mm512_set1_epi16(0x4200);
const __m512i a = _mm512_set1_epi16(0x00ff);
/* unpack leaves 128-bit lanes of four pixels as lo 0,2,4,6 and
* hi 1,3,5,7 (in blocks of four); these put the eight in order. */
const __m512i first = _mm512_set_epi64(11, 10, 3, 2, 9, 8, 1, 0);
const __m512i second = _mm512_set_epi64(15, 14, 7, 6, 13, 12, 5, 4);
for (h = 0; h < height;
h++, output += out_stride >> 2, input += in_stride >> 1)
{
int w = 0;
/* The last block takes the row's tail through masks: loads and
* stores past the row are suppressed, not issued. */
for (; w < width; w += 32)
{
__m512i lo, hi, c0, c2, in, r, g, b;
int left = width - w;
__mmask32 m_in = (left >= 32) ? (__mmask32)0xffffffffu
: (__mmask32)((1u << left) - 1u);
__mmask16 m_out0 = (left >= 16) ? (__mmask16)0xffff
: (__mmask16)((1u << left) - 1u);
__mmask16 m_out1 = (left >= 32) ? (__mmask16)0xffff
: (left > 16) ? (__mmask16)((1u << (left - 16)) - 1u)
: (__mmask16)0;
in = _mm512_maskz_loadu_epi16(m_in, input + w);
r = _mm512_mulhi_epi16(_mm512_and_si512(
_mm512_srl_epi16(in, rs), mask_r), mul_r);
g = _mm512_mulhi_epi16(_mm512_and_si512(in, mask_g), mul_g);
b = _mm512_mulhi_epi16(_mm512_and_si512(
_mm512_slli_epi16(in, 5), mask_b), mul_b);
c0 = swap_rb ? r : b;
c2 = swap_rb ? b : r;
lo = _mm512_or_si512(_mm512_unpacklo_epi8(c0, g),
_mm512_bslli_epi128(_mm512_unpacklo_epi8(c2, a), 2));
hi = _mm512_or_si512(_mm512_unpackhi_epi8(c0, g),
_mm512_bslli_epi128(_mm512_unpackhi_epi8(c2, a), 2));
_mm512_mask_storeu_epi32(output + w, m_out0,
_mm512_permutex2var_epi64(lo, first, hi));
_mm512_mask_storeu_epi32(output + w + 16, m_out1,
_mm512_permutex2var_epi64(lo, second, hi));
}
}
PIXCONV_ZEROUPPER();
}
#endif
/* Runs the conversion wide and returns 1, or returns 0 for the caller's
* own SSE2 path. */
static int pixconv_16to32_wide(void *output, const void *input,
int width, int height, int out_stride, int in_stride, int fmt)
{
switch (pixconv_simd_tier())
{
#ifdef PIXCONV_HAVE_AVX512
case 2:
pixconv_16to32_avx512((uint32_t*)output, (const uint16_t*)input,
width, height, out_stride, in_stride, fmt);
return 1;
#endif
case 1:
pixconv_16to32_avx2((uint32_t*)output, (const uint16_t*)input,
width, height, out_stride, in_stride, fmt);
return 1;
default:
break;
}
return 0;
}
#endif
void conv_0rgb1555_argb8888(void *output_, const void *input_,
int width, int height,
int out_stride, int in_stride)
@@ -178,6 +435,12 @@ void conv_0rgb1555_argb8888(void *output_, const void *input_,
int max_width = width - 7;
#endif
#ifdef PIXCONV_HAVE_WIDE
if (pixconv_16to32_wide(output_, input_, width, height,
out_stride, in_stride, PIXCONV_1555_ARGB))
return;
#endif
for (h = 0; h < height;
h++, output += out_stride >> 2, input += in_stride >> 1)
{
@@ -268,6 +531,12 @@ void conv_rgb565_argb8888(void *output_, const void *input_,
int max_width = width - 7;
#endif
#ifdef PIXCONV_HAVE_WIDE
if (pixconv_16to32_wide(output_, input_, width, height,
out_stride, in_stride, PIXCONV_565_ARGB))
return;
#endif
for (h = 0; h < height;
h++, output += out_stride >> 2, input += in_stride >> 1)
{
@@ -373,7 +642,13 @@ void conv_rgb565_abgr8888(void *output_, const void *input_,
#elif (defined(__ARM_NEON__) || defined(__ARM_NEON))
int max_width = width - 7;
#endif
for (h = 0; h < height;
#ifdef PIXCONV_HAVE_WIDE
if (pixconv_16to32_wide(output_, input_, width, height,
out_stride, in_stride, PIXCONV_565_ABGR))
return;
#endif
for (h = 0; h < height;
h++, output += out_stride >> 2, input += in_stride >> 1)
{
int w = 0;
+4
View File
@@ -20,6 +20,10 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* retro_environment.h spells MSVC's SSE/SSE2 targets as __SSE__/__SSE2__,
* which the SIMD paths below test. */
#include <retro_environment.h>
#include <gfx/scaler/scaler_int.h>
#include <string.h>
+16 -11
View File
@@ -2147,29 +2147,34 @@ static uint32_t spv_lvalue(struct spv_emit *e, uint32_t node, unsigned *sc, bool
const struct rslc_var *v = e->c->prog.vars[n->a];
if (e->vars[v->id].is_const)
{
/* A folded constant indexed dynamically lives in one
* variable initialised with the constant: Private for a
* global, Function for a local. */
/* A folded constant indexed dynamically needs storage.
* A global gets one Private variable initialised with the
* constant. A local gets a fresh Function variable at
* every access, stored right before it is read, as
* glslang's "indexable": the store stays inside the branch
* that reads it, and each variable is written once before
* its only read, so drivers can still fold it into a
* constant table. */
struct spv_var *sv = &e->vars[v->id];
uint32_t cid = spv_const_var(e, v);
unsigned vsc = v->global ? SPV_SC_PRIVATE : SPV_SC_FUNCTION;
*sc = vsc; *laid = false; *std140 = false;
if (!cid)
return 0;
if (!v->global)
{
uint32_t ptr = spv_local(e, v->type, v->name ? v->name->str : NULL, 0);
if (!ptr || !spv_store(e, ptr, cid))
return 0;
return ptr;
}
if (!sv->ptr)
{
uint32_t ptrt = spv_ptr_to(e, v->type, vsc);
if (!ptrt)
return 0;
sv->ptr = spv_id(e);
if (v->global)
{
if (!spv_inst4(e, SPV_SEC_TYPES, SpvOpVariable, ptrt, sv->ptr, vsc, cid))
return 0;
}
else if (!spv_w(e, &e->func_vars, (5u << 16) | SpvOpVariable)
|| !spv_w(e, &e->func_vars, ptrt) || !spv_w(e, &e->func_vars, sv->ptr)
|| !spv_w(e, &e->func_vars, vsc) || !spv_w(e, &e->func_vars, cid))
if (!spv_inst4(e, SPV_SEC_TYPES, SpvOpVariable, ptrt, sv->ptr, vsc, cid))
return 0;
if (v->name && !spv_name(e, sv->ptr, v->name->str))
return 0;
+331 -155
View File
@@ -506,57 +506,107 @@ static void SHA1Reset(struct sha1_state *context)
context->corrupted = 0;
}
/* The block functions take a run of whole blocks straight from the
* caller's buffer and keep the state in registers across it, with
* nothing copied into the context and the instruction set asked about
* once per run.
*
* The two-lane forms run two independent messages through the rounds
* side by side. A round waits on the one before it, and the hardware
* rounds have a latency of several cycles each, so a single message
* leaves the unit idle most of the time; a second one fills it. They
* serve SHA1DigestMany(). */
#if defined(SHA_HAVE_ARM_PATH)
SHA_TARGET_ARM
static void sha1_block_hw(uint32_t *state, const uint8_t *in)
static const uint32_t sha1_arm_k[4] =
{
static const uint32_t hw_k[4] =
0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6
};
/* Round group i of one lane: the schedule, then four rounds. e1 is
* taken from the current A, so it has to be read before the round
* overwrites it. */
#define SHA1_ARM_GROUP(i, m, abcd, e) \
do { \
uint32x4_t _w; \
uint32_t _e1; \
if ((i) >= 4) \
m[(i) & 3] = vsha1su1q_u32( \
vsha1su0q_u32(m[(i) & 3], m[((i) + 1) & 3], m[((i) + 2) & 3]), \
m[((i) + 3) & 3]); \
_w = vaddq_u32(m[(i) & 3], vdupq_n_u32(sha1_arm_k[(i) / 5])); \
_e1 = vsha1h_u32(vgetq_lane_u32(abcd, 0)); \
if ((i) < 5) \
abcd = vsha1cq_u32(abcd, e, _w); \
else if ((i) < 10 || (i) >= 15) \
abcd = vsha1pq_u32(abcd, e, _w); \
else \
abcd = vsha1mq_u32(abcd, e, _w); \
e = _e1; \
} while (0)
SHA_TARGET_ARM
static void sha1_hw_blocks(uint32_t *state, const uint8_t *in, size_t n)
{
uint32x4_t abcd = vld1q_u32(state);
uint32_t e = state[4];
while (n--)
{
0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6
};
uint32x4_t m[4], abcd, abcd_save;
uint32_t e, e_save, e1;
unsigned i;
uint32x4_t m[4], abcd_save = abcd;
uint32_t e_save = e;
unsigned i;
abcd = vld1q_u32(state);
e = state[4];
abcd_save = abcd;
e_save = e;
for (i = 0; i < 4; i++)
m[i] = vreinterpretq_u32_u8(vrev32q_u8(vld1q_u8(in + 16 * i)));
for (i = 0; i < 20; i++)
{
uint32x4_t w;
if (i >= 4)
m[i & 3] = vsha1su1q_u32(
vsha1su0q_u32(m[i & 3], m[(i + 1) & 3], m[(i + 2) & 3]),
m[(i + 3) & 3]);
w = vaddq_u32(m[i & 3], vdupq_n_u32(hw_k[i / 5]));
/* Taken from the current A, so it has to be read before the
* round overwrites it. */
e1 = vsha1h_u32(vgetq_lane_u32(abcd, 0));
if (i < 5)
abcd = vsha1cq_u32(abcd, e, w);
else if (i < 10 || i >= 15)
abcd = vsha1pq_u32(abcd, e, w);
else
abcd = vsha1mq_u32(abcd, e, w);
e = e1;
for (i = 0; i < 4; i++)
m[i] = vreinterpretq_u32_u8(vrev32q_u8(vld1q_u8(in + 16 * i)));
for (i = 0; i < 20; i++)
SHA1_ARM_GROUP(i, m, abcd, e);
abcd = vaddq_u32(abcd, abcd_save);
e += e_save;
in += 64;
}
vst1q_u32(state, abcd);
state[4] = e;
}
vst1q_u32(state, vaddq_u32(abcd, abcd_save));
state[4] = e + e_save;
SHA_TARGET_ARM
static void sha1_hw_blocks2(uint32_t *s0, uint32_t *s1,
const uint8_t *p0, const uint8_t *p1, size_t n)
{
uint32x4_t a0 = vld1q_u32(s0), a1 = vld1q_u32(s1);
uint32_t e0 = s0[4], e1 = s1[4];
while (n--)
{
uint32x4_t m0[4], m1[4], a0_save = a0, a1_save = a1;
uint32_t e0_save = e0, e1_save = e1;
unsigned i;
for (i = 0; i < 4; i++)
{
m0[i] = vreinterpretq_u32_u8(vrev32q_u8(vld1q_u8(p0 + 16 * i)));
m1[i] = vreinterpretq_u32_u8(vrev32q_u8(vld1q_u8(p1 + 16 * i)));
}
for (i = 0; i < 20; i++)
{
SHA1_ARM_GROUP(i, m0, a0, e0);
SHA1_ARM_GROUP(i, m1, a1, e1);
}
a0 = vaddq_u32(a0, a0_save);
a1 = vaddq_u32(a1, a1_save);
e0 += e0_save;
e1 += e1_save;
p0 += 64;
p1 += 64;
}
vst1q_u32(s0, a0);
vst1q_u32(s1, a1);
s0[4] = e0;
s1[4] = e1;
}
#elif defined(SHA_HAVE_X86_PATH)
/* SHA1RNDS4 takes the round function as an immediate, so the twenty
* groups are written out rather than looped. */
#define SHA1_X86_GROUP(f, idx) \
#define SHA1_X86_GROUP(f, idx, m, abcd, ea, eb) \
do { \
__m128i _w, _t; \
if ((idx) >= 4) \
@@ -574,50 +624,90 @@ static void sha1_block_hw(uint32_t *state, const uint8_t *in)
_t = ea; ea = eb; eb = _t; \
} while (0)
#define SHA1_X86_ROUNDS(G) \
G(0, 0); G(0, 1); G(0, 2); G(0, 3); G(0, 4); \
G(1, 5); G(1, 6); G(1, 7); G(1, 8); G(1, 9); \
G(2, 10); G(2, 11); G(2, 12); G(2, 13); G(2, 14); \
G(3, 15); G(3, 16); G(3, 17); G(3, 18); G(3, 19)
#define SHA1_X86_ONE(f, i) SHA1_X86_GROUP(f, i, m, abcd, ea, eb)
#define SHA1_X86_TWO(f, i) \
SHA1_X86_GROUP(f, i, m0, a0, ea0, eb0); \
SHA1_X86_GROUP(f, i, m1, a1, ea1, eb1)
/* A full 128-bit lane reverse rather than the per-word swap SHA-256
* uses: SHA1RNDS4 consumes the message words in the opposite order
* within the vector. */
#define SHA1_X86_MASK() _mm_set_epi64x( \
(long long)0x0001020304050607ULL, (long long)0x08090a0b0c0d0e0fULL)
SHA_TARGET_X86
static void sha1_block_hw(uint32_t *state, const uint8_t *in)
static void sha1_hw_blocks(uint32_t *state, const uint8_t *in, size_t n)
{
/* A full 128-bit lane reverse rather than the per-word swap SHA-256
* uses: SHA1RNDS4 consumes the message words in the opposite order
* within the vector. */
const __m128i mask = _mm_set_epi64x(
(long long)0x0001020304050607ULL, (long long)0x08090a0b0c0d0e0fULL);
__m128i m[4], abcd, ea, eb, abcd_save, e_save;
unsigned i;
abcd = _mm_shuffle_epi32(
const __m128i mask = SHA1_X86_MASK();
__m128i abcd = _mm_shuffle_epi32(
_mm_loadu_si128((const __m128i *)(const void *)state), 0x1B);
ea = _mm_set_epi32((int)state[4], 0, 0, 0);
__m128i e = _mm_set_epi32((int)state[4], 0, 0, 0);
abcd_save = abcd;
e_save = ea;
while (n--)
{
__m128i m[4], ea = e, eb, abcd_save = abcd;
unsigned i;
for (i = 0; i < 4; i++)
m[i] = _mm_shuffle_epi8(
_mm_loadu_si128((const __m128i *)(const void *)(in + 16 * i)), mask);
for (i = 0; i < 4; i++)
m[i] = _mm_shuffle_epi8(
_mm_loadu_si128((const __m128i *)(const void *)(in + 16 * i)), mask);
SHA1_X86_ROUNDS(SHA1_X86_ONE);
e = _mm_sha1nexte_epu32(ea, e);
abcd = _mm_add_epi32(abcd, abcd_save);
in += 64;
}
_mm_storeu_si128((__m128i *)(void *)state, _mm_shuffle_epi32(abcd, 0x1B));
state[4] = (uint32_t)_mm_extract_epi32(e, 3);
}
SHA1_X86_GROUP(0, 0); SHA1_X86_GROUP(0, 1);
SHA1_X86_GROUP(0, 2); SHA1_X86_GROUP(0, 3);
SHA1_X86_GROUP(0, 4); SHA1_X86_GROUP(1, 5);
SHA1_X86_GROUP(1, 6); SHA1_X86_GROUP(1, 7);
SHA1_X86_GROUP(1, 8); SHA1_X86_GROUP(1, 9);
SHA1_X86_GROUP(2, 10); SHA1_X86_GROUP(2, 11);
SHA1_X86_GROUP(2, 12); SHA1_X86_GROUP(2, 13);
SHA1_X86_GROUP(2, 14); SHA1_X86_GROUP(3, 15);
SHA1_X86_GROUP(3, 16); SHA1_X86_GROUP(3, 17);
SHA1_X86_GROUP(3, 18); SHA1_X86_GROUP(3, 19);
SHA_TARGET_X86
static void sha1_hw_blocks2(uint32_t *s0, uint32_t *s1,
const uint8_t *p0, const uint8_t *p1, size_t n)
{
const __m128i mask = SHA1_X86_MASK();
__m128i a0 = _mm_shuffle_epi32(
_mm_loadu_si128((const __m128i *)(const void *)s0), 0x1B);
__m128i a1 = _mm_shuffle_epi32(
_mm_loadu_si128((const __m128i *)(const void *)s1), 0x1B);
__m128i e0 = _mm_set_epi32((int)s0[4], 0, 0, 0);
__m128i e1 = _mm_set_epi32((int)s1[4], 0, 0, 0);
ea = _mm_sha1nexte_epu32(ea, e_save);
abcd = _mm_add_epi32(abcd, abcd_save);
while (n--)
{
__m128i m0[4], m1[4], ea0 = e0, ea1 = e1, eb0, eb1;
__m128i a0_save = a0, a1_save = a1;
unsigned i;
_mm_storeu_si128((__m128i *)(void *)state,
_mm_shuffle_epi32(abcd, 0x1B));
state[4] = (uint32_t)_mm_extract_epi32(ea, 3);
for (i = 0; i < 4; i++)
{
m0[i] = _mm_shuffle_epi8(
_mm_loadu_si128((const __m128i *)(const void *)(p0 + 16 * i)), mask);
m1[i] = _mm_shuffle_epi8(
_mm_loadu_si128((const __m128i *)(const void *)(p1 + 16 * i)), mask);
}
SHA1_X86_ROUNDS(SHA1_X86_TWO);
e0 = _mm_sha1nexte_epu32(ea0, e0);
e1 = _mm_sha1nexte_epu32(ea1, e1);
a0 = _mm_add_epi32(a0, a0_save);
a1 = _mm_add_epi32(a1, a1_save);
p0 += 64;
p1 += 64;
}
_mm_storeu_si128((__m128i *)(void *)s0, _mm_shuffle_epi32(a0, 0x1B));
_mm_storeu_si128((__m128i *)(void *)s1, _mm_shuffle_epi32(a1, 0x1B));
s0[4] = (uint32_t)_mm_extract_epi32(e0, 3);
s1[4] = (uint32_t)_mm_extract_epi32(e1, 3);
}
#endif
#ifdef SHA_NEED_SCALAR
static void SHA1ProcessMessageBlockScalar(struct sha1_state *context)
static void sha1_scalar_blocks(uint32_t *digest, const uint8_t *block, size_t n)
{
const unsigned K[] = /* Constants defined in SHA-1 */
{
@@ -631,96 +721,108 @@ static void SHA1ProcessMessageBlockScalar(struct sha1_state *context)
unsigned W[80]; /* Word sequence */
unsigned A, B, C, D, E; /* Word buffers */
/* Initialize the first 16 words in the array W */
for (t = 0; t < 16; t++)
for (; n; n--, block += 64)
{
W[t] = ((unsigned) context->block[t * 4]) << 24;
W[t] |= ((unsigned) context->block[t * 4 + 1]) << 16;
W[t] |= ((unsigned) context->block[t * 4 + 2]) << 8;
W[t] |= ((unsigned) context->block[t * 4 + 3]);
/* Initialize the first 16 words in the array W */
for (t = 0; t < 16; t++)
{
W[t] = ((unsigned) block[t * 4]) << 24;
W[t] |= ((unsigned) block[t * 4 + 1]) << 16;
W[t] |= ((unsigned) block[t * 4 + 2]) << 8;
W[t] |= ((unsigned) block[t * 4 + 3]);
}
for (t = 16; t < 80; t++)
W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
A = digest[0];
B = digest[1];
C = digest[2];
D = digest[3];
E = digest[4];
for (t = 0; t < 20; t++)
{
temp = SHA1CircularShift(5,A) +
((B & C) | ((~B) & D)) + E + W[t] + K[0];
temp &= 0xFFFFFFFF;
E = D;
D = C;
C = SHA1CircularShift(30,B);
B = A;
A = temp;
}
for (t = 20; t < 40; t++)
{
temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1];
temp &= 0xFFFFFFFF;
E = D;
D = C;
C = SHA1CircularShift(30,B);
B = A;
A = temp;
}
for (t = 40; t < 60; t++)
{
temp = SHA1CircularShift(5,A) +
((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
temp &= 0xFFFFFFFF;
E = D;
D = C;
C = SHA1CircularShift(30,B);
B = A;
A = temp;
}
for (t = 60; t < 80; t++)
{
temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];
temp &= 0xFFFFFFFF;
E = D;
D = C;
C = SHA1CircularShift(30,B);
B = A;
A = temp;
}
digest[0] =
(digest[0] + A) & 0xFFFFFFFF;
digest[1] =
(digest[1] + B) & 0xFFFFFFFF;
digest[2] =
(digest[2] + C) & 0xFFFFFFFF;
digest[3] =
(digest[3] + D) & 0xFFFFFFFF;
digest[4] =
(digest[4] + E) & 0xFFFFFFFF;
}
for (t = 16; t < 80; t++)
W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
A = context->digest[0];
B = context->digest[1];
C = context->digest[2];
D = context->digest[3];
E = context->digest[4];
for (t = 0; t < 20; t++)
{
temp = SHA1CircularShift(5,A) +
((B & C) | ((~B) & D)) + E + W[t] + K[0];
temp &= 0xFFFFFFFF;
E = D;
D = C;
C = SHA1CircularShift(30,B);
B = A;
A = temp;
}
for (t = 20; t < 40; t++)
{
temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1];
temp &= 0xFFFFFFFF;
E = D;
D = C;
C = SHA1CircularShift(30,B);
B = A;
A = temp;
}
for (t = 40; t < 60; t++)
{
temp = SHA1CircularShift(5,A) +
((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
temp &= 0xFFFFFFFF;
E = D;
D = C;
C = SHA1CircularShift(30,B);
B = A;
A = temp;
}
for (t = 60; t < 80; t++)
{
temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];
temp &= 0xFFFFFFFF;
E = D;
D = C;
C = SHA1CircularShift(30,B);
B = A;
A = temp;
}
context->digest[0] =
(context->digest[0] + A) & 0xFFFFFFFF;
context->digest[1] =
(context->digest[1] + B) & 0xFFFFFFFF;
context->digest[2] =
(context->digest[2] + C) & 0xFFFFFFFF;
context->digest[3] =
(context->digest[3] + D) & 0xFFFFFFFF;
context->digest[4] =
(context->digest[4] + E) & 0xFFFFFFFF;
}
#endif
static void SHA1ProcessMessageBlock(struct sha1_state *context)
/* Whole blocks from @in into @digest, the instruction set asked about
* once for the run. */
static void sha1_blocks(uint32_t *digest, const uint8_t *in, size_t n)
{
if (!n)
return;
#if defined(SHA_ARM_DISPATCH) || defined(SHA_X86_DISPATCH)
if (cpu_features_get() & RETRO_SIMD_SHA1)
sha1_block_hw(context->digest, context->block);
sha1_hw_blocks(digest, in, n);
else
SHA1ProcessMessageBlockScalar(context);
sha1_scalar_blocks(digest, in, n);
#elif defined(SHA_HAVE_ARM_PATH) || defined(SHA_HAVE_X86_PATH)
sha1_block_hw(context->digest, context->block);
sha1_hw_blocks(digest, in, n);
#else
SHA1ProcessMessageBlockScalar(context);
sha1_scalar_blocks(digest, in, n);
#endif
}
static void SHA1ProcessMessageBlock(struct sha1_state *context)
{
sha1_blocks(context->digest, context->block, 1);
context->block_index = 0;
}
@@ -825,6 +927,16 @@ static void SHA1Input(struct sha1_state *context,
{
unsigned l = 64 - (unsigned)context->block_index;
/* Whole blocks are hashed where they lie */
if (l == 64 && len >= 64)
{
unsigned whole = len & ~63u;
sha1_blocks(context->digest, message_array, whole / 64);
message_array += whole;
len -= whole;
continue;
}
if (len < l)
l = len;
@@ -871,15 +983,79 @@ void sha1_stream_block(struct sha1_state *p, const uint8_t *data)
SHA1ProcessMessageBlock(p);
}
static const uint32_t sha1_iv[5] =
{
0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0
};
/* The last @len % 64 octets of a @len octet message, at @tail, padded
* into one or two blocks at @out. Returns the blocks. */
static size_t sha1_pad(uint8_t *out, const uint8_t *tail, size_t len)
{
size_t rest = len % 64;
size_t n = rest < 56 ? 1 : 2;
uint64_t bits = (uint64_t)len << 3;
unsigned i;
if (rest)
memcpy(out, tail, rest);
out[rest] = 0x80;
memset(out + rest + 1, 0, n * 64 - rest - 1 - 8);
for (i = 0; i < 8; i++)
out[n * 64 - 1 - i] = (uint8_t)(bits >> (8 * i));
return n;
}
static void sha1_out(const uint32_t *state, uint8_t *digest)
{
unsigned i;
for (i = 0; i < 20; i++)
digest[i] = (uint8_t)(state[i >> 2] >> (8 * (3 - (i & 3))));
}
void SHA1Digest(const uint8_t* data, size_t len, uint8_t digest[20])
{
struct sha1_state sha;
uint32_t state[5];
uint8_t last[128];
size_t whole = len & ~(size_t)63;
SHA1Reset(&sha);
sha1_stream_update(&sha, data, len);
memcpy(state, sha1_iv, sizeof(state));
sha1_blocks(state, data, whole / 64);
sha1_blocks(state, last, sha1_pad(last, data + whole, len));
sha1_out(state, digest);
}
if (!SHA1Result(&sha, digest))
memset(digest, 0, 20);
void SHA1DigestMany(const uint8_t *data, size_t stride, size_t len,
unsigned n, uint8_t *digests)
{
unsigned i = 0;
#if defined(SHA_HAVE_ARM_PATH) || defined(SHA_HAVE_X86_PATH)
#if defined(SHA_ARM_DISPATCH) || defined(SHA_X86_DISPATCH)
if (cpu_features_get() & RETRO_SIMD_SHA1)
#endif
{
size_t whole = len & ~(size_t)63;
for (; i + 2 <= n; i += 2)
{
const uint8_t *p0 = data + (size_t)i * stride;
const uint8_t *p1 = p0 + stride;
uint32_t s0[5], s1[5];
uint8_t l0[128], l1[128];
size_t k;
memcpy(s0, sha1_iv, sizeof(s0));
memcpy(s1, sha1_iv, sizeof(s1));
sha1_hw_blocks2(s0, s1, p0, p1, whole / 64);
k = sha1_pad(l0, p0 + whole, len);
sha1_pad(l1, p1 + whole, len);
sha1_hw_blocks2(s0, s1, l0, l1, k);
sha1_out(s0, digests + 20 * (size_t)i);
sha1_out(s1, digests + 20 * (size_t)i + 20);
}
}
#endif
for (; i < n; i++)
SHA1Digest(data + (size_t)i * stride, len, digests + 20 * (size_t)i);
}
int sha1_calculate(const char *path, char *result)
+48 -8
View File
@@ -221,7 +221,8 @@ int r7z_archive_extract(r7z_archive_t *a, uint32_t index,
* folder's entire output - every member of a non-solid archive - is
* handed over as the decoded folder buffer itself, with nothing copied
* and nothing left in the folder cache. Any other entry extracts
* exactly as r7z_archive_extract() does.
* exactly as r7z_archive_extract() does. Either way the buffer has one
* octet more than @out_len, the caller's to write, as for a NUL.
*/
int r7z_archive_extract_detach(r7z_archive_t *a, uint32_t index,
uint8_t **out, size_t *out_len);
@@ -277,13 +278,13 @@ int r7z_archive_decode_step(r7z_archive_t *a, uint32_t index);
* @avail : receives how many of them are there to be read
*
* How much of entry @index has been decoded. A folder whose last coder
* decodes in order - LZMA or LZMA2, which is to say nearly all of them -
* fills the buffer it will be held in from the front, and what is in it
* is final: @data points into it, at the same place from the first call
* to the last and for as long as the folder is held, and @avail grows
* to the entry's size. A folder that ends in a filter gives nothing
* until it is done, and then everything. The bytes are not checked
* against a CRC until the folder is.
* decodes in order - LZMA, LZMA2, PPMd, Deflate or BZip2, which is to
* say nearly all of them - fills the buffer it will be held in from the
* front, and what is in it is final: @data points into it, at the same
* place from the first call to the last and for as long as the folder
* is held, and @avail grows to the entry's size. A folder that ends in
* a filter gives nothing until it is done, and then everything. The
* bytes are not checked against a CRC until the folder is.
*
* Any thread.
*
@@ -292,6 +293,45 @@ int r7z_archive_decode_step(r7z_archive_t *a, uint32_t index);
int r7z_archive_decode_peek(const r7z_archive_t *a, uint32_t index,
const uint8_t **data, size_t *avail);
/**
* r7z_archive_stream_begin:
* @a : opened archive
* @index : the entry to stream out
*
* Starts entry @index out of its folder a piece at a time, for one that
* is too big to have whole in memory, or in a folder that is. A folder
* of LZMA, LZMA2, PPMd, Deflate, Deflate64, BZip2 or Copy, with up to
* three branch converters or Delta after it, decodes in pieces - LZMA
* through a window of its dictionary's size - and a BCJ2 folder its
* main input so, the other three whole. One entry is streamed at a
* time, beside whatever else the archive is doing; after one is all
* out, an entry later in the same folder goes on from where it ended,
* so a folder's entries streamed in order decode it once.
*
* Returns: R7Z_OK, R7Z_ERROR_UNSUPPORTED for a folder of another shape,
* which the entry is decoded whole from instead, or another negative
* R7Z_ERROR_* code.
*/
int r7z_archive_stream_begin(r7z_archive_t *a, uint32_t index);
/**
* r7z_archive_stream_step:
* @data : receives where the next of the entry's octets are, until
* the next call
* @len : receives how many, which may be none
*
* Decodes the next piece of the folder, up to 256 KiB.
*
* Returns: R7Z_PENDING with a piece, R7Z_OK once the entry is all out
* and checked against its CRC, or a negative R7Z_ERROR_* code, which
* ends the stream.
*/
int r7z_archive_stream_step(r7z_archive_t *a, const uint8_t **data,
size_t *len);
/* Ends a stream, freeing its window. */
void r7z_archive_stream_end(r7z_archive_t *a);
RETRO_END_DECLS
#endif
+33
View File
@@ -164,6 +164,39 @@ int r7z_bcj2_decode_part(r7z_bcj2_state_t *st,
const uint8_t *jump_buf, size_t jump_len,
const uint8_t *rc_buf, size_t rc_len);
/* One of the four inputs a piece at a time: @len octets at @p, @pos of
* them taken, and @more set while more of it is to come */
typedef struct r7z_bcj2_in
{
const uint8_t *p;
size_t len;
size_t pos;
int more;
} r7z_bcj2_in_t;
/**
* r7z_bcj2_decode_in:
* @st : conversion state, prepared with r7z_bcj2_state_init()
* @dst : where this piece of the output goes, @dst_cap octets of
* room, more than 4
* @wrote : receives how many octets went there
* @main_in .. @rc_in : the four inputs, each as far as it has come
*
* As r7z_bcj2_decode_part(), with every input and the output a piece at
* a time: converts until @dst has no room left for a branch, the main
* input is used up, or a branch candidate needs more of the range
* coder's input or of its targets' than is there while more is to come,
* which it waits for unread. Each input's pos says how far it was taken; the
* caller hands over more of the ones that ran short and calls again,
* and at the end sees that the targets were all used.
*
* Returns: RBCJ2_PENDING, or a negative RBCJ2_ERROR_* code.
*/
int r7z_bcj2_decode_in(r7z_bcj2_state_t *st,
uint8_t *dst, size_t dst_cap, size_t *wrote,
r7z_bcj2_in_t *main_in, r7z_bcj2_in_t *call_in,
r7z_bcj2_in_t *jump_in, r7z_bcj2_in_t *rc_in);
/**
* r7z_bcj2_decode:
* @dst : output buffer
+2
View File
@@ -87,6 +87,8 @@ size_t rfilters_armt_decode (uint32_t ip, uint8_t *data, size_t len);
size_t rfilters_ppc_decode (uint32_t ip, uint8_t *data, size_t len);
size_t rfilters_sparc_decode(uint32_t ip, uint8_t *data, size_t len);
size_t rfilters_ia64_decode (uint32_t ip, uint8_t *data, size_t len);
/* ARM64: BL, and ADRP within +-512 MiB of the instruction */
size_t rfilters_arm64_decode(uint32_t ip, uint8_t *data, size_t len);
RETRO_END_DECLS
+5 -4
View File
@@ -169,16 +169,17 @@ int rlzma_stream_init(rlzma_stream_t *s, const uint8_t *props,
* rlzma_stream_reset:
* @s : initialized decoder state
*
* Resets the range coder, the probability model and the dictionary
* position, keeping the buffers bound by rlzma_stream_init(). Call this
* before the first block and at every LZMA2 state reset.
* Resets the range coder, the probability model and the dictionary,
* output starting again at the dictionary's start, keeping the buffers
* bound by rlzma_stream_init(). Call this before the first block.
*/
void rlzma_stream_reset(rlzma_stream_t *s);
/**
* rlzma_stream_reset_parts:
* @s : initialized decoder state
* @init_dic : non-zero to reset the dictionary position
* @init_dic : non-zero to forget what is in the dictionary; output
* goes on where it was
* @init_state : non-zero to reset the range coder, probabilities and
* match history
*
+13 -1
View File
@@ -30,11 +30,23 @@
RETRO_BEGIN_DECLS
#if defined(__SSE2__)
/* SSE2 wherever the whole build has it (all x86-64, MSVC included,
* and 32-bit x86 built with it), NEON on ARMv8 and ARM builds with
* it, the C loop elsewhere. AUDIO_MIX_NO_SIMD leaves them out. */
#if !defined(AUDIO_MIX_NO_SIMD) && (defined(__SSE2__) || defined(_M_X64) \
|| (defined(_M_IX86_FP) && _M_IX86_FP >= 2))
#define AUDIO_MIX_HAVE_SSE2 1
#define audio_mix_volume audio_mix_volume_SSE2
void audio_mix_volume_SSE2(float *out,
const float *in, float vol, size_t samples);
#elif !defined(AUDIO_MIX_NO_SIMD) && (defined(__ARM_NEON) \
|| defined(__ARM_NEON__) || defined(_M_ARM64))
#define AUDIO_MIX_HAVE_NEON 1
#define audio_mix_volume audio_mix_volume_NEON
void audio_mix_volume_NEON(float *out,
const float *in, float vol, size_t samples);
#else
#define audio_mix_volume audio_mix_volume_C
#endif
+1
View File
@@ -48,6 +48,7 @@ RETRO_BEGIN_DECLS
#define RESAMPLER_SIMD_AVX2 (1 << 12)
#define RESAMPLER_SIMD_VFPU (1 << 13)
#define RESAMPLER_SIMD_PS (1 << 14)
#define RESAMPLER_SIMD_AVX512 (1 << 22)
enum resampler_quality
{
+117
View File
@@ -176,6 +176,44 @@ int crypto_aes_hw(void);
void aes_ctr_crypt(const struct aes_ctx *ctx, uint8_t *counter,
const uint8_t *in, uint8_t *out, size_t len);
/**
* aes_cbc_encrypt:
* @iv : 16 octets; left holding the last ciphertext
* block, so a caller can continue the chain.
* @len : a multiple of 16.
*
* CBC encryption. @in and @out may alias.
**/
void aes_cbc_encrypt(const struct aes_ctx *ctx, uint8_t *iv,
const uint8_t *in, uint8_t *out, size_t len);
/**
* aes_cbc_decrypt:
* @iv : 16 octets; left holding the last ciphertext
* block, so a caller can continue the chain.
* @len : a multiple of 16.
*
* CBC decryption, several blocks at once where the cipher runs on
* instructions made for it. @in and @out may alias.
**/
void aes_cbc_decrypt(const struct aes_ctx *ctx, uint8_t *iv,
const uint8_t *in, uint8_t *out, size_t len);
/**
* aes_cbc_encrypt_lanes:
* @n : independent chains, each @len octets (a
* multiple of 16) from in[i] to out[i] under
* iv[i], which is left as aes_cbc_encrypt()
* leaves it.
*
* A CBC chain waits on the cipher block by block; where the cipher
* runs on instructions made for it, independent chains are interleaved
* so their waits overlap.
**/
void aes_cbc_encrypt_lanes(const struct aes_ctx *ctx, unsigned n,
uint8_t *const *iv, const uint8_t *const *in, uint8_t *const *out,
size_t len);
/**
* aes_cmac:
*
@@ -185,6 +223,21 @@ void aes_ctr_crypt(const struct aes_ctx *ctx, uint8_t *counter,
void aes_cmac(const struct aes_ctx *ctx,
const uint8_t *msg, size_t len, uint8_t *mac);
/* CMAC over a message given in pieces, which need not be blocks:
* init, update per piece, final for the tag. aes_cmac() is these
* three over one piece. */
struct aes_cmac_ctx
{
const struct aes_ctx *aes;
size_t n; /* octets held in buf */
uint8_t x[16];
uint8_t buf[16];
};
void aes_cmac_init(struct aes_cmac_ctx *s, const struct aes_ctx *aes);
void aes_cmac_update(struct aes_cmac_ctx *s, const uint8_t *msg, size_t len);
void aes_cmac_final(struct aes_cmac_ctx *s, uint8_t *mac);
/**
* aes_ccm_encrypt:
* @nonce_len : 7 to 13 octets (SMB3 uses 11).
@@ -214,6 +267,40 @@ int aes_ccm_decrypt(const struct aes_ctx *ctx,
const uint8_t *ct, size_t ct_len,
const uint8_t *tag, size_t tag_len, uint8_t *pt);
/* CCM over a message given in pieces, in order, which need not be
* blocks: init with the whole message length, then encrypt or
* decrypt each piece (in place or not), then the tag. The one-shot
* calls above are these over one piece. */
struct aes_ccm_stream
{
const struct aes_ctx *aes;
size_t len; /* message length given at init */
size_t off; /* octets processed */
size_t L;
size_t tag_len;
uint8_t x[16]; /* CBC-MAC chain */
uint8_t ctr[16];
uint8_t ks[16]; /* keystream of the current block */
uint8_t s0[16];
};
/**
* aes_ccm_stream_init:
*
* Returns: 0, or -1 on a bad nonce or tag length or a @msg_len too
* long for the nonce.
**/
int aes_ccm_stream_init(struct aes_ccm_stream *s, const struct aes_ctx *ctx,
const uint8_t *nonce, size_t nonce_len,
const uint8_t *aad, size_t aad_len,
size_t msg_len, size_t tag_len);
void aes_ccm_stream_encrypt(struct aes_ccm_stream *s,
const uint8_t *pt, uint8_t *ct, size_t len);
void aes_ccm_stream_decrypt(struct aes_ccm_stream *s,
const uint8_t *ct, uint8_t *pt, size_t len);
/* @tag_len octets, once every octet of the message has been given */
void aes_ccm_stream_tag(struct aes_ccm_stream *s, uint8_t *tag);
/**
* aes_gcm_init:
*
@@ -251,6 +338,36 @@ int aes_gcm_decrypt(const struct aes_gcm_ctx *ctx,
const uint8_t *ct, size_t ct_len,
const uint8_t *tag, uint8_t *pt);
/* GCM with its two halves apart, over pieces that need not be blocks:
* the AAD and then the ciphertext are hashed piece by piece, in order,
* for the tag; aes_gcm_stream_crypt() runs the counter mode at any
* octet offset of the ciphertext, so pieces can be decrypted where
* they lie and in any order - after the tag is checked, as
* aes_gcm_decrypt() does. GMAC is the AAD alone. */
struct aes_gcm_stream
{
const struct aes_gcm_ctx *g;
uint64_t y_hi;
uint64_t y_lo;
uint64_t aad_len;
uint64_t ct_len;
size_t part_len; /* octets held in part */
uint8_t j0[16];
uint8_t part[16];
};
void aes_gcm_stream_init(struct aes_gcm_stream *s,
const struct aes_gcm_ctx *ctx, const uint8_t *nonce, size_t nonce_len);
/* all of the AAD before any ciphertext */
void aes_gcm_stream_aad(struct aes_gcm_stream *s,
const uint8_t *aad, size_t len);
void aes_gcm_stream_hash_ct(struct aes_gcm_stream *s,
const uint8_t *ct, size_t len);
void aes_gcm_stream_tag(struct aes_gcm_stream *s, uint8_t *tag);
/* @len octets at octet @off of the ciphertext; @in and @out may alias */
void aes_gcm_stream_crypt(const struct aes_gcm_stream *s, uint64_t off,
const uint8_t *in, uint8_t *out, size_t len);
/* ChaCha20 (RFC 8439): 256-bit key, 96-bit nonce, 32-bit block counter. */
#define CHACHA20_KEY_SIZE 32
+64
View File
@@ -87,6 +87,19 @@ void rinflate_reset(void *stream, int window_bits);
void rinflate_set_in(void *stream, const uint8_t *in, size_t in_size);
void rinflate_set_out(void *stream, uint8_t *out, size_t out_size);
/**
* rinflate_extend_out:
* @stream : stream returned by rinflate_new()
* @out_size : the output's new size, from where rinflate_set_out()
* put its start
*
* Moves the end of the bound output, keeping what is written and where:
* a decode into one buffer runs a part at a time, its matches reaching
* back into the buffer itself. A size short of what is written is
* ignored.
**/
void rinflate_extend_out(void *stream, size_t out_size);
/**
* rinflate_set_dictionary:
*
@@ -110,6 +123,57 @@ void rinflate_set_start_bit(void *stream, int bits);
* one of the RDEFLATE_PROCESS_* codes. */
int rinflate_process(void *stream, size_t *read, size_t *wrote);
/**
* rinflate64_decode:
* @dst : where the stream decodes to, @dst_len octets of room
* @src : a raw Deflate64 stream (no header), @src_len octets
* @wrote : receives the octets decoded (may be NULL)
*
* Deflate64 - RFC 1951 with a 64 KiB window, a 16-bit length extension
* and distance codes 30 and 31 - decoded whole, in one call, into a
* buffer that holds all of it, as zip (method 9) and 7z store it.
*
* Returns: RDEFLATE_PROCESS_END, or RDEFLATE_PROCESS_ERROR for a stream
* that is malformed, ends early or decodes to more than @dst_len.
**/
int rinflate64_decode(uint8_t *dst, size_t dst_len,
const uint8_t *src, size_t src_len, size_t *wrote);
/* What rinflate64_process() leaves unread of an input that is not the
* last: fewer octets than this, which the next call is given again in
* front of what follows them. */
#define RINFLATE64_LOOKAHEAD 1024
/**
* rinflate64_new:
*
* A Deflate64 decoder that runs a piece at a time: into a buffer of
* the caller's that need not hold the whole stream, from input that
* can arrive in parts. It keeps the last 64 KiB it decoded for the
* matches that reach back into it. NULL when out of memory.
**/
void *rinflate64_new(void);
void rinflate64_free(void *stream);
/**
* rinflate64_process:
* @in : the next of the stream, @in_len octets
* @final : nonzero when nothing follows @in
* @consumed : receives how many of @in were taken (may be NULL); an
* input that is not the last is taken up to fewer than
* RINFLATE64_LOOKAHEAD octets from its end, and the rest
* goes first in the next call's
* @out : where to decode to, @out_len octets of room
* @wrote : receives the octets decoded into @out (may be NULL)
*
* Returns: RDEFLATE_PROCESS_END at the end of the stream,
* RDEFLATE_PROCESS_NEXT when @out is full or more input is wanted, or
* RDEFLATE_PROCESS_ERROR.
**/
int rinflate64_process(void *stream, const uint8_t *in, size_t in_len,
int final, size_t *consumed, uint8_t *out, size_t out_len,
size_t *wrote);
/* -------- compression (deflate) -------- */
/* level: 0 (store) .. 9 (maximum). */
+187
View File
@@ -0,0 +1,187 @@
/* Copyright (C) 2010-2026 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (rbzip2.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* Clean-room decoder for the bzip2 stream format.
*
* A stream is "BZh", a block size digit, then blocks: each the
* Burrows-Wheeler transform of up to 100 000 x digit octets, coded as
* move-to-front ranks with runs of rank 0 in bijective base 2 and two
* to six Huffman tables, under a run-length coding of the input
* applied before the transform. Each block carries the CRC of what it
* decodes to, and the end of the stream the combination of them all;
* both are checked.
*
* Decoding only, of one whole stream held in memory: into a buffer
* already sized for it, as the WIA disc image format and zip archives
* store theirs, or into one grown as it goes, for a .bz2 file. Blocks marked randomised, which no bzip2 has written since
* 0.9.5, are refused. No file I/O. */
#ifndef __LIBRETRO_SDK_FORMAT_RBZIP2_H__
#define __LIBRETRO_SDK_FORMAT_RBZIP2_H__
#include <stddef.h>
#include <stdint.h>
#include <retro_common_api.h>
RETRO_BEGIN_DECLS
#define RBZIP2_PENDING 1 /* rbzip2_step(): a block, and more to come */
#define RBZIP2_OK 0
#define RBZIP2_ERROR_DATA -1 /* not a stream, corrupt, or a CRC fails */
#define RBZIP2_ERROR_SPACE -2 /* decodes to more than the buffer holds */
#define RBZIP2_ERROR_MEM -3
typedef struct rbzip2 rbzip2_t;
/* A decoder; its block buffer is allocated at the first block and kept
* for the next stream. NULL when out of memory. */
rbzip2_t *rbzip2_new(void);
void rbzip2_free(rbzip2_t *bz);
/**
* rbzip2_decode:
* @dst : where the stream decodes to
* @dst_len : capacity of @dst
* @src : the stream, from "BZh" on
* @src_len : octets at @src; anything after the stream's end is ignored
* @wrote : receives the octets decoded (may be NULL)
*
* Returns: RBZIP2_OK, or an RBZIP2_ERROR_* value.
**/
int rbzip2_decode(rbzip2_t *bz, uint8_t *dst, size_t dst_len,
const uint8_t *src, size_t src_len, size_t *wrote);
/**
* rbzip2_begin:
* @dst : where the stream decodes to
* @dst_len : capacity of @dst
* @src : the stream, from "BZh" on, which stays where it is
* until the stream is done
* @src_len : octets at @src
*
* The same, a block at a time: binds the stream and its output, for
* rbzip2_step() to decode. A block is at most 900 000 octets.
*
* Returns: RBZIP2_OK, or an RBZIP2_ERROR_* value.
**/
int rbzip2_begin(rbzip2_t *bz, uint8_t *dst, size_t dst_len,
const uint8_t *src, size_t src_len);
/**
* rbzip2_step:
* @wrote : receives the octets decoded so far (may be NULL), what is
* in @dst before them final
*
* Decodes the next block of the stream rbzip2_begin() bound, its CRC
* checked, or reads the end of the stream and checks the CRC of them
* all.
*
* Returns: RBZIP2_PENDING after a block, RBZIP2_OK at the end, or an
* RBZIP2_ERROR_* value.
**/
int rbzip2_step(rbzip2_t *bz, size_t *wrote);
/**
* rbzip2_begin_blocks:
* @src : the stream, from "BZh" on, which stays where it is until
* the stream is done
* @src_len : octets at @src
*
* The same, each block into the decoder's own buffer, for a stream
* whose whole output is not wanted in one place. The buffer grows to
* the largest block, which the run-length coding before the transform
* lets reach 51 times the block size.
*
* Returns: RBZIP2_OK, or an RBZIP2_ERROR_* value.
**/
int rbzip2_begin_blocks(rbzip2_t *bz, const uint8_t *src, size_t src_len);
/**
* rbzip2_begin_ring:
* @a : the first of the stream, at least its four-octet header
* @b : and what comes after @a, as where a ring buffer wraps; or
* none, @b_len 0
*
* As rbzip2_begin_blocks(), for a stream given a part at a time, in a
* ring buffer the caller reads into: before each rbzip2_step_block()
* the input is either the rest of the stream or at least
* RBZIP2_AHEAD octets of it, handed over again with rbzip2_input().
*
* Returns: RBZIP2_OK, or an RBZIP2_ERROR_* value.
**/
int rbzip2_begin_ring(rbzip2_t *bz, const uint8_t *a, size_t a_len,
const uint8_t *b, size_t b_len);
/* The most of the stream one block can take up: 900000 symbols of up to
* twenty bits, the tables and the selectors before them, and the
* stream's end */
#define RBZIP2_AHEAD (2304u * 1024u)
/**
* rbzip2_input:
*
* The input from where rbzip2_taken() says the stream has been taken
* to: @a, then @b where the ring wraps (@b_len 0 for none).
**/
void rbzip2_input(rbzip2_t *bz, const uint8_t *a, size_t a_len,
const uint8_t *b, size_t b_len);
/**
* rbzip2_taken:
*
* Returns: how many octets of the stream the decoder has taken, which
* it does not need again.
**/
uint64_t rbzip2_taken(const rbzip2_t *bz);
/**
* rbzip2_step_block:
* @block : receives where the block's octets are, until the next call
* @len : receives how many
*
* The next block of the stream rbzip2_begin_blocks() bound, its CRC
* checked, or the end of the stream and the CRC of them all.
*
* Returns: RBZIP2_PENDING with a block, RBZIP2_OK at the end, or an
* RBZIP2_ERROR_* value.
**/
int rbzip2_step_block(rbzip2_t *bz, const uint8_t **block, size_t *len);
/**
* rbzip2_decode_alloc:
* @dst : receives the decoded octets, allocated with malloc() and
* the caller's to free; one octet past them is room to spare
* @wrote : receives how many there are
*
* The same, for a stream whose size is not known: the buffer grows as
* blocks are decoded into it.
*
* Returns: RBZIP2_OK, or an RBZIP2_ERROR_* value, with *@dst NULL.
**/
int rbzip2_decode_alloc(rbzip2_t *bz, const uint8_t *src, size_t src_len,
uint8_t **dst, size_t *wrote);
RETRO_END_DECLS
#endif
+73 -8
View File
@@ -54,7 +54,7 @@
* compressed size before it allocates.
* This is the only caller that compresses,
* and the reason the encoder exists at all.
* cheevos/cheevos_rvz.c decompresses one-shot, and reads a
* formats/rvz/rvz.c decompresses one-shot, and reads a
* frame's declared content size to size
* its buffer.
* file/archive_file_zstd.c decompresses an archive member, and
@@ -177,15 +177,80 @@ int rzstd_encode(uint8_t *dst, size_t dst_len,
* implementation's default. */
#define RZSTD_WINDOW_LOG_MAX 27
/* No streaming interface is offered.
/* -------- a frame a block at a time -------- */
typedef struct rzstd_blocks rzstd_blocks_t;
/* the most rzstd_blocks_want() asks for: a block of 128 KiB, and the
* next block's header or the frame's checksum */
#define RZSTD_WANT_MAX (128 * 1024 + 4)
/**
* rzstd_blocks_begin:
* @src : one complete frame, which stays where it is until the
* stream is freed
* @src_len : its exact length
*
* One was declared here, both directions, for a caller that cannot hold
* a whole frame. No caller in this tree is one: both CHD readers hold a
* hunk entire, and every other user above starts from a complete frame.
* Eleven entry points were declared and none was ever implemented:
* nothing would have noticed them breaking, because nothing called
* them. Absent is honest where declared was not.
* A frame decoded a block at a time, for one too big to have whole in
* memory: through a ring of its window and two blocks more, at least a
* megabyte, which the matches reach back into. Each block comes out
* whole where it was decoded.
*
* Returns: the stream, or NULL when @src is not a frame or its window
* cannot be had.
*/
rzstd_blocks_t *rzstd_blocks_begin(const uint8_t *src, size_t src_len);
/**
* rzstd_blocks_step:
* @data : receives where the block's octets are, until the next
* call
* @len : receives how many
*
* Returns: RZSTD_PROCESS_NEXT with a block, RZSTD_PROCESS_END once the
* frame is all out and its size and end are as they should be, or
* RZSTD_PROCESS_ERROR.
*/
int rzstd_blocks_step(rzstd_blocks_t *z, const uint8_t **data, size_t *len);
/**
* rzstd_blocks_open:
*
* A frame decoded a block at a time, as for rzstd_blocks_begin(), from
* input handed over a piece at a time: what rzstd_blocks_want() asks
* for each time, which is the next block and the next block's header.
* Each piece is decoded where it is, so it can be read straight into a
* buffer of RZSTD_WANT_MAX octets that is used over again.
*
* Returns: the stream, or NULL when there is no memory.
*/
rzstd_blocks_t *rzstd_blocks_open(void);
/**
* rzstd_blocks_want:
*
* Returns: how many octets of the frame rzstd_blocks_give() is to be
* handed next, or 0 once the frame is all out.
*/
size_t rzstd_blocks_want(const rzstd_blocks_t *z);
/**
* rzstd_blocks_give:
* @src : the next rzstd_blocks_want() octets of the frame, which
* need not stay where they are after the call
* @len : how many
* @data : receives where the octets it decoded are, until the next
* call; there may be none
* @dlen : receives how many
*
* Returns: RZSTD_PROCESS_NEXT, or RZSTD_PROCESS_ERROR. Once
* rzstd_blocks_want() is 0 the frame is all out, its size and checksum
* as they should be.
*/
int rzstd_blocks_give(rzstd_blocks_t *z, const uint8_t *src, size_t len,
const uint8_t **data, size_t *dlen);
void rzstd_blocks_free(rzstd_blocks_t *z);
/**
* rzstd_error_name:
+89
View File
@@ -0,0 +1,89 @@
/* Copyright (C) 2010-2026 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (xxh3.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef _LIBRETRO_ENCODINGS_XXH3_H
#define _LIBRETRO_ENCODINGS_XXH3_H
#include <stdint.h>
#include <stddef.h>
#include <retro_common_api.h>
RETRO_BEGIN_DECLS
/**
* xxh3_64:
* @data : the octets
* @len : how many
* @seed : the seed; 0 is the unseeded hash
*
* The 64-bit XXH3 of @data, as xxHash specifies it (XXH3_64bits and
* XXH3_64bits_withSeed), with its default secret. Inputs over 240
* octets go through AVX-512, AVX2 or SSE2 where the CPU has them,
* chosen once, or NEON; the value is the same every way and on every
* host.
*
* Returns: the hash.
*/
uint64_t xxh3_64(const void *data, size_t len, uint64_t seed);
/* The same hash taken over pieces handed to it one after another,
* XXH3_64bits_update() and its digest. Pieces are taken where they lie
* a 64-octet stripe at a time; at most 1024 octets are held between
* calls, after the last stripe taken. */
typedef struct xxh3_state
{
uint64_t acc[8]; /* the accumulators */
uint64_t total; /* octets taken */
uint64_t seed;
uint8_t sec[192]; /* the seeded secret, with a seed */
uint8_t buf[64 + 1024]; /* the last stripe taken, then what is held */
uint32_t held; /* how much is held */
unsigned pos; /* the next stripe's place in its block */
/* what takes the pieces past what is held, on this CPU's path */
void (*take)(struct xxh3_state *s, const uint8_t *p, size_t len);
} xxh3_state_t;
/**
* xxh3_reset:
*
* Starts @s over with @seed: nothing taken yet.
*/
void xxh3_reset(xxh3_state_t *s, uint64_t seed);
/**
* xxh3_update:
*
* Takes @len more octets at @data, which may be any length.
*/
void xxh3_update(xxh3_state_t *s, const void *data, size_t len);
/**
* xxh3_digest:
*
* Returns: xxh3_64() of what @s has taken, which may go on taking more.
*/
uint64_t xxh3_digest(const xxh3_state_t *s);
RETRO_END_DECLS
#endif
+79
View File
@@ -0,0 +1,79 @@
/* Copyright (C) 2010-2026 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (xxh64.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef _LIBRETRO_ENCODINGS_XXH64_H
#define _LIBRETRO_ENCODINGS_XXH64_H
#include <stdint.h>
#include <stddef.h>
#include <retro_common_api.h>
RETRO_BEGIN_DECLS
/* XXH64, as xxHash specifies it: the hash Zstandard frames are checked
* with, and the same value for the same octets and seed on every
* host. */
/* A hash taken over pieces handed to it one after another */
typedef struct xxh64_state
{
uint64_t v[4]; /* the four lanes */
uint64_t total; /* octets taken */
uint8_t mem[32]; /* a stripe not yet whole */
uint32_t memsize; /* how much of it there is */
} xxh64_state_t;
/**
* xxh64:
* @data : the octets
* @len : how many
* @seed : the seed
*
* Returns: the XXH64 of @data.
*/
uint64_t xxh64(const void *data, size_t len, uint64_t seed);
/**
* xxh64_reset:
*
* Starts @s over with @seed: nothing taken yet.
*/
void xxh64_reset(xxh64_state_t *s, uint64_t seed);
/**
* xxh64_update:
*
* Takes @len more octets at @data, which may be any length.
*/
void xxh64_update(xxh64_state_t *s, const void *data, size_t len);
/**
* xxh64_digest:
*
* Returns: the XXH64 of what @s has taken, which may go on taking more.
*/
uint64_t xxh64_digest(const xxh64_state_t *s);
RETRO_END_DECLS
#endif
+38 -8
View File
@@ -93,6 +93,7 @@ typedef struct file_archive_transfer
* pending_active is what makes this visible to the iterate loop;
* everything else is the state that call needs to resume. */
file_archive_file_handle_t pending_handle;
uint64_t pending_size;
/* The member's output, opened beside pending_path and renamed over
* it when the member completes (filestream_open_atomic()). A
* backend that can produce output a piece at a time writes it here
@@ -100,7 +101,6 @@ typedef struct file_archive_transfer
* pending_handle.data, which is written here at the end. */
struct RFILE *pending_sink;
char pending_path[PATH_MAX_LENGTH];
uint32_t pending_size;
bool pending_active;
/* The scan asked to stop while a member was still parked. The stop
* is honoured once the pending decode has drained, not before, or
@@ -147,11 +147,14 @@ struct archive_extract_userdata
char current_file_path[PATH_MAX_LENGTH];
bool found_file;
bool list_only;
/* The member's CRC is wanted: a backend whose archive does not
* record one decodes the member to have it */
bool crc_wanted;
};
/* Returns true when parsing should continue. False to stop. */
typedef int (*file_archive_file_cb)(const char *name, const char *valid_exts,
const uint8_t *cdata, unsigned cmode, uint32_t csize, uint32_t size,
const uint8_t *cdata, unsigned cmode, uint64_t csize, uint64_t size,
uint32_t crc32, struct archive_extract_userdata *userdata);
struct file_archive_file_backend
@@ -169,7 +172,7 @@ struct file_archive_file_backend
bool (*stream_decompress_data_to_file_init)(
void *context, file_archive_file_handle_t *handle,
const uint8_t *cdata, unsigned cmode, uint32_t csize, uint32_t size);
const uint8_t *cdata, unsigned cmode, uint64_t csize, uint64_t size);
int (*stream_decompress_data_to_file_iterate)(
void *context,
file_archive_file_handle_t *handle);
@@ -251,7 +254,7 @@ int64_t file_archive_read_at(void *ud, uint64_t off, void *dst, size_t len);
* failure.
*/
int file_archive_perform_mode_start(const char *name, const char *valid_exts,
const uint8_t *cdata, unsigned cmode, uint32_t csize, uint32_t size,
const uint8_t *cdata, unsigned cmode, uint64_t csize, uint64_t size,
uint32_t crc32, struct archive_extract_userdata *userdata);
/**
@@ -265,7 +268,7 @@ int file_archive_perform_mode_start(const char *name, const char *valid_exts,
int file_archive_perform_mode_step(file_archive_transfer_t *state);
bool file_archive_perform_mode(const char *name, const char *valid_exts,
const uint8_t *cdata, unsigned cmode, uint32_t csize, uint32_t size,
const uint8_t *cdata, unsigned cmode, uint64_t csize, uint64_t size,
uint32_t crc32, struct archive_extract_userdata *userdata);
int file_archive_compressed_read(
@@ -279,6 +282,28 @@ const struct file_archive_file_backend* file_archive_get_rar_file_backend(void);
const struct file_archive_file_backend* file_archive_get_file_backend(const char *path);
/**
* file_archive_open_hints:
* @size : size of the archive, or <= 0 if unknown
*
* Returns: the hints an archive of @size is opened with, asking for a
* mapping where the host can afford one.
**/
unsigned file_archive_open_hints(int64_t size);
/**
* file_archive_whole:
* @size : octets in @file
* @owned : receives a buffer for the caller to
* free, or NULL
*
* Returns: all of @file, where the stream lends it whole (a mapping, a
* read-ahead window) or else read into *@owned; NULL on failure. What
* is lent holds until the next call on @file.
**/
const uint8_t *file_archive_whole(struct RFILE *file, int64_t size,
void **owned);
/**
* file_archive_get_file_crc32:
* @path : filename path of archive
@@ -303,6 +328,7 @@ uint32_t file_archive_get_file_crc32_and_size(const char *path, uint64_t *size);
extern const struct file_archive_file_backend zlib_backend;
extern const struct file_archive_file_backend sevenzip_backend;
extern const struct file_archive_file_backend zstd_backend;
extern const struct file_archive_file_backend bz2_backend;
extern const struct file_archive_file_backend rar_backend;
/* ---- incremental archive entry source ----
@@ -317,9 +343,13 @@ extern const struct file_archive_file_backend rar_backend;
*
* Contract: dst regions across successive read() calls must be
* sequential and contiguous (the data_transfer source fill satisfies
* this naturally); the built-in inflate binds its output window once
* and the source verifies the cursor. Returns bytes produced, 0 at
* end of entry, -1 on error. Only backends whose entries are
* this naturally), and dst has room to the entry's end: n paces the
* decode, and a read may produce more - the built-in inflate a whole
* input slice, a Zstandard or bzip2 member, which does not stop part
* way, all of itself straight into dst at the first read. The
* built-in inflate binds its output window once and the source
* verifies the cursor. Returns bytes produced, 0 at end of entry, -1
* on error. Only backends whose entries are
* independently decodable provide sources (ZIP; 7z solid blocks
* cannot), so a NULL open is a normal "use the classic whole-entry
* path" signal, not an error. */
+2
View File
@@ -274,6 +274,8 @@ typedef struct rh265_hw_ops
const rh265_access_unit_t *au, const rh265_nal_t *slices,
const rh265_hw_picture_t *picture);
int (*output)(void *decoder, unsigned slot, video_hw_frame_t *frame);
/* Optional EOF submission: 0 accepted, 1 busy, negative failure. */
int (*drain)(void *decoder);
} rh265_hw_ops_t;
typedef struct rh265_hw_backend
+1 -1
View File
@@ -179,7 +179,7 @@ void rmp4_video_stream_set_output(rmp4_video_stream_t *stream,
int rmp4_video_stream_set_output_stride(rmp4_video_stream_t *stream,
uint32_t *out, unsigned stride);
/* H.264/HEVC/AV1 on the hardware backend: true when the
/* H.264/HEVC/VP9/AV1 on the hardware backend: true when the
* frames will come from it, false when they stay in software - another
* codec, a colour the GPU frame cannot carry, or a backend that will
* not take the stream. A stream already under way starts over from
+16
View File
@@ -52,6 +52,9 @@ typedef struct rvp9_hw_backend
bool (*open_config)(void *user, const rvp9_hdr *header,
const video_color_metadata_t *color, const rvp9_config_t *config,
unsigned slots, rvp9_hw_ops_t *ops);
/* Same-format key-frame restart: 0 ready, 1 pending, negative failure.
* Discard old work without invalidating exported leases. */
int (*restart)(void *decoder);
} rvp9_hw_backend_t;
RETRO_BEGIN_DECLS
@@ -111,6 +114,19 @@ static INLINE bool rvp9_hw_session_init(rvp9_hw_session_t *s,
return rvp9_hw_session_init_mode(s, ops, slots, false);
}
static INLINE bool rvp9_hw_session_restart(rvp9_hw_session_t *s)
{
rvp9_hw_ops_t ops;
unsigned slots;
bool packet_decoder;
if (!s)
return false;
ops = s->ops;
slots = s->slots;
packet_decoder = s->packet_decoder;
return rvp9_hw_session_init_mode(s, &ops, slots, packet_decoder);
}
static INLINE void rvp9_hw_session_clear_packet(rvp9_hw_session_t *s)
{
s->packet = NULL; s->packet_size = 0; s->phase = 0;
+144
View File
@@ -0,0 +1,144 @@
/* Copyright (C) 2010-2026 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (rvz.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef __LIBRETRO_SDK_FORMAT_RVZ_H
#define __LIBRETRO_SDK_FORMAT_RVZ_H
#include <stddef.h>
#include <stdint.h>
#include <retro_common_api.h>
RETRO_BEGIN_DECLS
/* RVZ and WIA, the GameCube and Wii disc image formats, read as the
* disc image they stand for: every read returns the octets an ISO of
* the same disc holds at that offset. Wii partitions, stored decrypted
* and without their hashes, come back hashed and encrypted, with the
* file's hash exceptions applied; padding stored as a generator seed
* comes back as the padding.
*
* Groups stored uncompressed, with bzip2, LZMA or LZMA2, with
* Zstandard (RVZ, needing HAVE_RZSTD) or PURGE (WIA) are read.
*
* The reader does no file I/O of its own and holds no file. Each range
* of the file it needs is described to the caller as a request, which
* names where the octets go: the caller puts up to @length octets from
* @offset there, however it gets them, and says how many with
* rvz_feed(). A short supply is legal and asked for again; none at all
* fails the operation.
*
* rvz_request_t req;
* int err;
*
* while ((err = rvz_open_step(rvz, &req)) == RVZ_PENDING)
* rvz_feed(rvz, pull(req.offset, req.dst, req.length));
*
* rvz_read_begin(rvz, offset, dst, len);
* while ((err = rvz_read_step(rvz, &req)) == RVZ_PENDING)
* rvz_feed(rvz, pull(req.offset, req.dst, req.length));
*
* Where a read can be served from the file as it is, the request's
* @dst is the read's own destination, so nothing is copied twice. */
#define RVZ_OK 0
#define RVZ_PENDING 1
#define RVZ_ERROR_DATA (-1)
#define RVZ_ERROR_PARAM (-2)
#define RVZ_ERROR_MEM (-3)
#define RVZ_ERROR_UNSUPPORTED (-4)
#define RVZ_ERROR_STATE (-5)
#define RVZ_DISC_GAMECUBE 1
#define RVZ_DISC_WII 2
typedef struct rvz rvz_t;
/**
* rvz_request_t:
*
* A range of the file the reader needs, and where to put it. Valid
* until the next call on the same rvz_t.
*/
typedef struct rvz_request
{
uint64_t offset; /* in the file */
size_t length; /* octets wanted; fewer may be supplied */
uint8_t *dst; /* where they go */
} rvz_request_t;
rvz_t *rvz_new(void);
void rvz_free(rvz_t *rvz);
/**
* rvz_open_step:
*
* Reads the file's heads and tables.
*
* Returns: RVZ_OK once the image is open, RVZ_PENDING with @req filled
* in when it needs a range of the file, or a negative RVZ_ERROR_* code
* when the file is not one this reads.
**/
int rvz_open_step(rvz_t *rvz, rvz_request_t *req);
/**
* rvz_feed:
* @len : octets put at the last request's @dst
*
* Returns: RVZ_OK, RVZ_ERROR_DATA when @len is 0 (the operation is
* then over), or RVZ_ERROR_STATE with no request outstanding.
**/
int rvz_feed(rvz_t *rvz, size_t len);
/* RVZ_DISC_GAMECUBE, RVZ_DISC_WII, or 0 when the file does not say */
int rvz_disc_type(const rvz_t *rvz);
/* the size of the disc image */
uint64_t rvz_disc_size(const rvz_t *rvz);
/**
* rvz_read_begin:
*
* Arms a read of @len octets of the disc image at @off into @dst,
* short at the end of the image. Nothing is read until
* rvz_read_step(). An armed read abandons one still in progress.
*
* Returns: RVZ_OK, or RVZ_ERROR_STATE before the image is open.
**/
int rvz_read_begin(rvz_t *rvz, uint64_t off, void *dst, size_t len);
/**
* rvz_read_step:
*
* Returns: RVZ_OK when the read is complete, RVZ_PENDING with @req
* filled in when it needs a range of the file, or a negative
* RVZ_ERROR_* code.
**/
int rvz_read_step(rvz_t *rvz, rvz_request_t *req);
/* octets the armed read has put at its @dst so far */
size_t rvz_read_progress(const rvz_t *rvz);
RETRO_END_DECLS
#endif
+1 -6
View File
@@ -24,6 +24,7 @@
#define __LIBRETRO_SDK_RJNI_H__
#include <jni.h>
#include <jni/rjni_env.h>
#include <retro_common_api.h>
#include <retro_atomic.h>
@@ -52,12 +53,6 @@ void rjni_set_vm(JavaVM *vm);
JavaVM *rjni_vm(void);
/* The calling thread's JNIEnv, attaching it to the VM on first use.
* Returns NULL if no VM is registered or the attach fails. A thread
* already attached by the VM (a Java-created thread) is recognised and
* left attached at exit; only threads this function attached are
* detached by the TLS destructor. */
JNIEnv *rjni_env(void);
/* Nonzero, after describing and clearing it, if an exception was
* pending. Uses ExceptionCheck, so no local reference is created. */
+12
View File
@@ -0,0 +1,12 @@
/* Copyright (C) 2010-2026 The RetroArch team.
* SPDX-License-Identifier: MIT */
#ifndef __LIBRETRO_SDK_RJNI_ENV_H__
#define __LIBRETRO_SDK_RJNI_ENV_H__
#include <jni.h>
#include <retro_common_api.h>
RETRO_BEGIN_DECLS
/* Calling thread's JNIEnv, attaching on demand; NULL without a VM or on failure. */
JNIEnv *rjni_env(void);
RETRO_END_DECLS
#endif
+61 -1
View File
@@ -4496,6 +4496,12 @@ enum retro_hw_render_interface_type
*/
RETRO_HW_RENDER_INTERFACE_GSKIT_PS2 = 5,
/**
* PlayStation 3 RSX (PSL1GHT).
* @see retro_hw_render_interface_rsx in libretro_rsx.h
*/
RETRO_HW_RENDER_INTERFACE_RSX = 6,
/** @private Defined to ensure <tt>sizeof(retro_hw_render_interface_type) == sizeof(int)</tt>.
* Do not use. */
RETRO_HW_RENDER_INTERFACE_DUMMY = INT_MAX
@@ -5560,7 +5566,9 @@ struct retro_log_callback
/** Indicates CPU support for the ASIMD instruction set. */
#define RETRO_SIMD_ASIMD (1 << 21)
/** Indicates CPU support for the AVX512 instruction set. */
/** Indicates CPU support for the AVX-512 x86-64-v4 baseline:
* AVX-512 F, CD, BW, DQ and VL are all present and the OS saves
* ZMM state. */
#define RETRO_SIMD_AVX512 (1 << 22)
/** Indicates CPU support for the LZCNT instruction (x86 ABM / ARM CLZ). */
@@ -5962,6 +5970,55 @@ enum retro_sensor_action
#define RETRO_SENSOR_ILLUMINANCE 6
/** @} */
/** @defgroup RETRO_SENSOR_SUBDEVICE Sensor Sub-Device Index
* @{
*/
/**
* The number of bits that a sub-device index is shifted by within a sensor ID
* or a \c retro_sensor_action.
*
* Addresses more than one sensor of the same kind on a single port.
* Index 0 denotes the controller itself and encodes to the values already in
* use (<tt>RETRO_SENSOR_SUBDEVICE(0, id) == id</tt>). Index 1 denotes the part
* of the controller held in the left hand: a Wii Remote's Nunchuk, or the
* left Joy-Con of a pair held as one controller (whose index 0 is the right
* one). Higher indices are left for controllers with more parts.
*
* A core learns whether the frontend has a sub-device's sensor from the
* enable action, \c retro_set_sensor_state_t returning \c true for it; a
* reading of 0 says nothing, as a frontend without it returns 0 for every
* read. For instance:
* @code
* bool nunchuk = sensor.set_sensor_state(port, (enum retro_sensor_action)
* RETRO_SENSOR_SUBDEVICE(1, RETRO_SENSOR_ACCELEROMETER_ENABLE), 100);
* float x = sensor.get_sensor_input(port,
* RETRO_SENSOR_SUBDEVICE(1, RETRO_SENSOR_ACCELEROMETER_X));
* @endcode
*
* @note \c retro_sensor_action is an enum, so an encoded action must be cast at
* the call site. \c RETRO_SENSOR_DUMMY pins the underlying type to \c int, so
* the encoded value is in range.
*
* @see RETRO_SENSOR_ID
* @see retro_sensor_action
*/
#define RETRO_SENSOR_INDEX_SHIFT 8
/** Mask of the bits below the sub-device index. */
#define RETRO_SENSOR_INDEX_MASK ((1u << RETRO_SENSOR_INDEX_SHIFT) - 1u)
/** Returns the sub-device index encoded in \c id; 0 is the controller itself. */
#define RETRO_SENSOR_INDEX(id) ((unsigned)(id) >> RETRO_SENSOR_INDEX_SHIFT)
/** Returns the sensor ID or action in \c id, without its sub-device index. */
#define RETRO_SENSOR_BASE(id) ((unsigned)(id) & RETRO_SENSOR_INDEX_MASK)
/** Addresses sensor \c id on sub-device \c index of a port. */
#define RETRO_SENSOR_SUBDEVICE(index, id) \
((unsigned)((((unsigned)(index)) << RETRO_SENSOR_INDEX_SHIFT) | ((unsigned)(id))))
/** @} */
/**
* Adjusts the state of a sensor.
*
@@ -6589,6 +6646,9 @@ enum retro_hw_context_type
/* Direct3D9, see RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE */
RETRO_HW_CONTEXT_D3D9 = 10,
/* PlayStation 3 RSX, see libretro_rsx.h */
RETRO_HW_CONTEXT_RSX = 11,
/** Dummy value to ensure sizeof(enum retro_hw_context_type) == sizeof(int). Do not use. */
RETRO_HW_CONTEXT_DUMMY = INT_MAX
};
+68
View File
@@ -0,0 +1,68 @@
/* Copyright (C) 2010-2026 The RetroArch team
*
* ---------------------------------------------------------------------------------------------
* The following license statement only applies to this libretro API header (libretro_rsx.h)
* ---------------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the
* "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef LIBRETRO_RSX_H_
#define LIBRETRO_RSX_H_
#include "libretro.h"
#define RETRO_HW_RENDER_INTERFACE_RSX_VERSION 1
/* Linear A8R8G8B8 texture in RSX local memory. The core owns the allocation
* until context_destroy; commands and presentation use the same FIFO. */
struct retro_rsx_image
{
uint32_t offset;
uint32_t pitch;
uint32_t width;
uint32_t height;
};
/* Query with GET_HW_RENDER_INTERFACE from context_reset, after requesting
* RETRO_HW_CONTEXT_RSX through SET_HW_RENDER. Valid until context_destroy.
* Both callers must submit commands on the main thread.
* Labels 198 and 199 belong to the frontend. */
struct retro_hw_render_interface_rsx
{
enum retro_hw_render_interface_type interface_type;
unsigned interface_version;
void *handle;
void *context;
/* The frontend owns this mapping; only [io_used, io_size) is available
* to the core. Do not free it or replace its command-buffer callback. */
void *io_address;
uint32_t io_size;
uint32_t io_used;
/* Publish an image before video_refresh(RETRO_HW_FRAME_BUFFER_VALID).
* NULL releases the cached image. No allocation or CPU wait is performed. */
bool (*set_image)(void *handle, const struct retro_rsx_image *image);
/* Transfers ownership of an rsxMemalign allocation to the frontend,
* freed once the commands submitted before the next video_refresh have
* finished. NULL is accepted. Returns false if the queue is full. */
bool (*retire_memory)(void *handle, void *memory);
};
#endif /* LIBRETRO_RSX_H_ */
+16
View File
@@ -126,6 +126,22 @@ void sha512_stream_final(struct sha512_state *p, uint8_t *digest);
**/
void SHA1Digest(const uint8_t* data, size_t len, uint8_t digest[20]);
/**
* SHA1DigestMany:
* @data : the first message
* @stride : from each message to the next
* @len : each message's length
* @n : the messages
* @digests : 20 octets for each, one after another
*
* SHA1Digest() of @n messages of the same length, the i-th at
* @data + i * @stride. Where the hardware does the rounds, two run
* side by side, which is close to twice as fast as one after the
* other.
**/
void SHA1DigestMany(const uint8_t *data, size_t stride, size_t len,
unsigned n, uint8_t *digests);
/**
* Streaming SHA-1, alongside the one-shot SHA1Digest() above.
*/
+22 -3
View File
@@ -189,9 +189,8 @@ const rrar_entry_t *rrar_archive_entry(const rrar_archive_t *a,
* @out_len : receives the entry's size
*
* Unpacks one entry and checks it against the archive's CRC-32, if the
* archive has one for it. The
* caller frees *@out. An empty entry yields a valid one-byte buffer and
* a length of zero.
* archive has one for it. The caller frees *@out, which has one octet
* more than *@out_len, the caller's to write, as for a NUL.
*
* Returns: RRAR_OK, or a negative RRAR_ERROR_* code, in which case
* *@out is NULL.
@@ -233,6 +232,26 @@ typedef struct rrar_watch
int rrar_archive_extract_to(rrar_archive_t *a, uint32_t index,
uint8_t *dst, size_t dst_size, const rrar_watch_t *watch);
/**
* rrar_archive_extract_sink:
* @a : opened archive
* @index : entry index
* @sink : handed the entry's octets in order, as they are final;
* returns nonzero to have the decode stop
* @ud : for @sink
*
* The same as rrar_archive_extract(), for a member too big to have
* whole in memory: one bigger than its window or 5 MiB, whichever is
* more, and a little over 2 MiB more is unpacked round a ring of that
* size. The CRC is checked once all of it has been handed over: on an
* error what @sink was given is not to be kept.
*
* Returns: RRAR_OK, RRAR_ERROR_CANCELLED if @sink stopped it, or
* another negative RRAR_ERROR_* code.
*/
int rrar_archive_extract_sink(rrar_archive_t *a, uint32_t index,
int (*sink)(void *ud, const uint8_t *data, size_t len), void *ud);
RETRO_END_DECLS
#endif
+14
View File
@@ -98,6 +98,20 @@ void intfstream_rewind(intfstream_internal_t *intf);
int64_t intfstream_tell(intfstream_internal_t *intf);
/**
* intfstream_view:
*
* intfstream_read() without the copy: points @ptr at up to @len
* octets at the stream position, where the stream holds them, and
* moves past them as a read of that many would.
*
* Returns: the octets lent, 0 at the end, or -1 when the stream holds
* nothing there it can lend; the caller then reads. Lent octets stay
* valid until the next call on @intf and must not be written.
*/
int64_t intfstream_view(intfstream_internal_t *intf, uint64_t len,
const uint8_t **ptr);
int intfstream_eof(intfstream_internal_t *intf);
void intfstream_putc(intfstream_internal_t *intf, int c);
+1 -1
View File
@@ -164,7 +164,7 @@ struct libretro_vfs_implementation_file
/* CreateFileMapping handle backing `mapped`; NULL when unmapped */
HANDLE map_handle;
#if !defined(_XBOX) && !defined(HAVE_MMAP)
void *warm; /* pre-Windows 8 read-ahead thread, NULL until used */
void *warm; /* mapped read-ahead thread, NULL until used */
#endif
#endif
char *buf;
+7 -4
View File
@@ -106,10 +106,13 @@ int64_t retro_vfs_file_view_impl(libretro_vfs_implementation_file *stream,
*
* Asks the OS to start bringing the range into memory, so a read or a
* touch of the mapping that follows finds it resident instead of
* stalling on the disk. Returns at once: madvise(MADV_WILLNEED) or
* PrefetchVirtualMemory() on a mapped file, posix_fadvise(WILLNEED) on
* a descriptor; a no-op where neither exists, on a scheme that is not
* a local file, or when the OS declines. A hint, never an error. */
* stalling on the disk. Returns at once: madvise(MADV_WILLNEED) on a
* mapped file, posix_fadvise(WILLNEED) on a descriptor; on Windows the
* range is handed to the file's read-ahead thread, which calls
* PrefetchVirtualMemory() (or touches the pages before Windows 8), so
* the caller never waits on the disk. A no-op where none of these
* exists, on a scheme that is not a local file, or when the OS
* declines. A hint, never an error. */
void retro_vfs_file_prefetch_impl(
libretro_vfs_implementation_file *stream, uint64_t offset,
uint64_t len);
+373 -36
View File
@@ -348,51 +348,105 @@ static int rsmb_recv_raw(struct rsmb_ctx *c, size_t *len)
/* ---- signing and sealing ------------------------------------------ */
/* The 16-octet signature of msg (its signature field zeroed), by the
* connection's algorithm: HMAC-SHA256 on 2.x, AES-CMAC on 3.x, AES-GMAC
* on 3.1.1 when the server chose it - the nonce being the MessageId and
* then the sender (1 for the server) and CANCEL bits. */
static void rsmb_mac(struct rsmb_ctx *c, const uint8_t *msg, size_t len,
int from_server, uint8_t *mac)
/* A message in pieces: a READ reply's payload received where the
* caller wants it, its header and any trailer in c->rx. */
struct rsmb_seg
{
const uint8_t *p;
size_t len;
};
/* The 16-octet signature of a message (its signature field zeroed),
* by the connection's algorithm: HMAC-SHA256 on 2.x, AES-CMAC on 3.x,
* AES-GMAC on 3.1.1 when the server chose it - the nonce being the
* MessageId and then the sender (1 for the server) and CANCEL bits.
* The header is in the first piece; one piece takes the one-shot. */
static void rsmb_mac(struct rsmb_ctx *c, const struct rsmb_seg *seg,
unsigned n, int from_server, uint8_t *mac)
{
const uint8_t *msg = seg[0].p;
unsigned i;
if (c->dialect == DIALECT_311 && c->sign_alg == SIGN_AES_GMAC)
{
uint8_t nonce[12];
memcpy(nonce, msg + 24, 8);
smb_put32(nonce + 8, (from_server ? 1u : 0u)
| (smb_get16(msg + 12) == SMB2_CANCEL_CMD ? 2u : 0u));
aes_gcm_encrypt(&c->sign_gcm, nonce, 12, msg, len, NULL, 0, NULL, mac);
if (n == 1)
aes_gcm_encrypt(&c->sign_gcm, nonce, 12, msg, seg[0].len,
NULL, 0, NULL, mac);
else
{
struct aes_gcm_stream gs;
aes_gcm_stream_init(&gs, &c->sign_gcm, nonce, 12);
for (i = 0; i < n; i++)
aes_gcm_stream_aad(&gs, seg[i].p, seg[i].len);
aes_gcm_stream_tag(&gs, mac);
crypto_memzero(&gs, sizeof(gs));
}
}
else if (c->dialect >= DIALECT_300)
{
struct aes_ctx a;
aes_init(&a, c->signing_key, 16);
aes_cmac(&a, msg, len, mac);
if (n == 1)
aes_cmac(&a, msg, seg[0].len, mac);
else
{
struct aes_cmac_ctx m;
aes_cmac_init(&m, &a);
for (i = 0; i < n; i++)
aes_cmac_update(&m, seg[i].p, seg[i].len);
aes_cmac_final(&m, mac);
}
crypto_memzero(&a, sizeof(a));
}
else if (n == 1)
hmac_sha256(c->signing_key, 16, msg, seg[0].len, mac);
else
hmac_sha256(c->signing_key, 16, msg, len, mac);
{
struct hmac_sha256_ctx h;
hmac_sha256_init(&h, c->signing_key, 16);
for (i = 0; i < n; i++)
hmac_sha256_update(&h, seg[i].p, seg[i].len);
hmac_sha256_final(&h, mac);
}
}
static void rsmb_sign(struct rsmb_ctx *c, uint8_t *msg, size_t len)
{
uint8_t mac[32];
uint8_t mac[32];
struct rsmb_seg seg;
smb_put32(msg + 16, smb_get32(msg + 16) | SMB2_FLAGS_SIGNED);
memset(msg + 48, 0, 16);
rsmb_mac(c, msg, len, 0, mac);
seg.p = msg;
seg.len = len;
rsmb_mac(c, &seg, 1, 0, mac);
memcpy(msg + 48, mac, 16);
}
/* @hdr is the first piece's octets, writable: the signature field is
* zeroed for the MAC and put back. */
static int rsmb_verify_segs(struct rsmb_ctx *c, uint8_t *hdr,
const struct rsmb_seg *seg, unsigned n)
{
uint8_t got[16], mac[32];
if (!(smb_get32(hdr + 16) & SMB2_FLAGS_SIGNED))
return -1;
memcpy(got, hdr + 48, 16);
memset(hdr + 48, 0, 16);
rsmb_mac(c, seg, n, 1, mac);
memcpy(hdr + 48, got, 16);
return crypto_memeq_ct(got, mac, 16) ? 0 : -1;
}
static int rsmb_verify_sig(struct rsmb_ctx *c, uint8_t *msg, size_t len)
{
uint8_t got[16], mac[32];
if (!(smb_get32(msg + 16) & SMB2_FLAGS_SIGNED))
return -1;
memcpy(got, msg + 48, 16);
memset(msg + 48, 0, 16);
rsmb_mac(c, msg, len, 1, mac);
memcpy(msg + 48, got, 16);
return crypto_memeq_ct(got, mac, 16) ? 0 : -1;
struct rsmb_seg seg;
seg.p = msg;
seg.len = len;
return rsmb_verify_segs(c, msg, &seg, 1);
}
/* Wraps msg (len octets, in c->tx after the transform header slot)
@@ -525,21 +579,294 @@ static int rsmb_send_msg(struct rsmb_ctx *c, size_t body_len, int preauth,
return rsmb_send_raw(c, msg, len);
}
/* What a fetch has in flight: a READ reply to one of these requests
* is received with its data at out + offs[slot], where the caller
* wants it, rather than in c->rx. */
struct rsmb_route
{
const uint64_t *mids;
const size_t *offs;
const size_t *lens;
uint8_t *out;
unsigned n;
unsigned slot; /* the request a reply's data went to; n if none */
uint32_t placed; /* how much went there */
};
/* the header and a READ response up to its Buffer */
#define RSMB_READ_HEAD (SMB2_HDR_SIZE + 16)
static int rsmb_recv_bytes(struct rsmb_ctx *c, uint8_t *dst, size_t len)
{
if (len && socket_receive_all_blocking_with_timeout(c->fd, dst, len,
(int)c->timeout * 1000) <= 0)
{
rsmb_err(c, "receive failed or timed out");
return -1;
}
return 0;
}
/* The request a plain header @h of an @len-octet message answers, if
* it is a successful READ whose data can be placed; its data's offset
* and length. -1 when the message is to be received whole. */
static int rsmb_route_find(const struct rsmb_route *rt, const uint8_t *h,
size_t len, uint32_t *doff, uint32_t *dlen)
{
const uint8_t *r = h + SMB2_HDR_SIZE;
uint64_t mid;
unsigned i;
if ( h[0] != 0xfe
|| smb_get16(h + 12) != SMB2_READ
|| smb_get32(h + 8) != STATUS_SUCCESS
|| smb_get32(h + 20) != 0 /* NextCommand */
|| smb_get16(r) != 17)
return -1;
*doff = r[2];
*dlen = smb_get32(r + 4);
if (*doff < RSMB_READ_HEAD || !*dlen || *doff > len || *dlen > len - *doff)
return -1;
mid = smb_get64(h + 24);
for (i = 0; i < rt->n; i++)
if (rt->mids[i] == mid)
break;
if (i == rt->n || *dlen > rt->lens[i])
return -1;
return (int)i;
}
/* A sealed message of @n octets, its first four in c->rx: received,
* authenticated and unsealed, a placeable READ reply's data straight
* into its request's place - decrypted there in place, after the tag
* (GCM) or with the MAC (CCM). c->rx is left with the plain header
* and any trailer after it. */
static int rsmb_recv_sealed(struct rsmb_ctx *c, size_t n, size_t *len,
struct rsmb_route *rt)
{
uint8_t *th = c->rx;
uint8_t *msg = c->rx + SMB2_TRANSFORM_HDR_SIZE;
uint8_t *dst = NULL;
size_t mlen, head, tail = 0;
uint32_t doff = 0, dlen = 0;
uint8_t tag[16];
int slot = -1, ok;
if ( !c->encrypt || n < SMB2_TRANSFORM_HDR_SIZE + SMB2_HDR_SIZE
|| rsmb_recv_bytes(c, th + 4, SMB2_TRANSFORM_HDR_SIZE - 4) != 0)
goto bad;
mlen = smb_get32(th + 36);
if (mlen != n - SMB2_TRANSFORM_HDR_SIZE || smb_get64(th + 44) != c->session_id)
goto bad;
head = mlen < RSMB_READ_HEAD ? mlen : RSMB_READ_HEAD;
if (rsmb_recv_bytes(c, msg, head) != 0)
return -1;
if (c->cipher == CIPHER_AES128_GCM)
{
struct aes_gcm_ctx g;
struct aes_gcm_stream gs;
uint8_t plain[256]; /* doff is one octet */
aes_gcm_init(&g, c->dec_key, 16);
aes_gcm_stream_init(&gs, &g, th + 20, 12);
aes_gcm_stream_aad(&gs, th + 20, 32);
/* the header decrypted aside to route by; the ciphertext stays
* for the tag */
aes_gcm_stream_crypt(&gs, 0, msg, plain, head);
if (head < mlen)
slot = rsmb_route_find(rt, plain, mlen, &doff, &dlen);
if (slot < 0)
{
crypto_memzero(&g, sizeof(g));
crypto_memzero(&gs, sizeof(gs));
crypto_memzero(plain, sizeof(plain));
if (rsmb_recv_bytes(c, msg + head, mlen - head) != 0)
return -1;
*len = n;
if (rsmb_unseal(c, len) != 0)
goto bad;
return 0;
}
dst = rt->out + rt->offs[slot];
tail = mlen - doff - dlen;
if ( rsmb_recv_bytes(c, msg + head, doff - head) != 0
|| rsmb_recv_bytes(c, dst, dlen) != 0
|| rsmb_recv_bytes(c, msg + doff, tail) != 0)
return -1;
aes_gcm_stream_hash_ct(&gs, msg, doff);
aes_gcm_stream_hash_ct(&gs, dst, dlen);
aes_gcm_stream_hash_ct(&gs, msg + doff, tail);
aes_gcm_stream_tag(&gs, tag);
ok = crypto_memeq_ct(tag, th + 4, 16);
if (ok)
{
aes_gcm_stream_crypt(&gs, 0, msg, plain, doff);
aes_gcm_stream_crypt(&gs, doff, dst, dst, dlen);
aes_gcm_stream_crypt(&gs, (uint64_t)doff + dlen,
msg + doff, msg + doff, tail);
memcpy(c->rx, plain, doff);
memmove(c->rx + doff, msg + doff, tail);
}
crypto_memzero(&g, sizeof(g));
crypto_memzero(&gs, sizeof(gs));
crypto_memzero(plain, sizeof(plain));
}
else
{
/* CCM authenticates the plaintext, so the message is decrypted
* in order as it arrives, the data in its place */
struct aes_ctx a;
struct aes_ccm_stream cs;
if ( aes_init(&a, c->dec_key, 16) != 0
|| aes_ccm_stream_init(&cs, &a, th + 20, 11, th + 20, 32,
mlen, 16) != 0)
goto bad;
aes_ccm_stream_decrypt(&cs, msg, msg, head);
if (head < mlen)
slot = rsmb_route_find(rt, msg, mlen, &doff, &dlen);
if (slot < 0)
{
if (rsmb_recv_bytes(c, msg + head, mlen - head) != 0)
return -1;
aes_ccm_stream_decrypt(&cs, msg + head, msg + head, mlen - head);
doff = (uint32_t)mlen;
}
else
{
dst = rt->out + rt->offs[slot];
tail = mlen - doff - dlen;
if (rsmb_recv_bytes(c, msg + head, doff - head) != 0)
return -1;
aes_ccm_stream_decrypt(&cs, msg + head, msg + head, doff - head);
if (rsmb_recv_bytes(c, dst, dlen) != 0)
return -1;
aes_ccm_stream_decrypt(&cs, dst, dst, dlen);
if (rsmb_recv_bytes(c, msg + doff, tail) != 0)
return -1;
aes_ccm_stream_decrypt(&cs, msg + doff, msg + doff, tail);
}
aes_ccm_stream_tag(&cs, tag);
ok = crypto_memeq_ct(tag, th + 4, 16);
crypto_memzero(&a, sizeof(a));
if (ok)
memmove(c->rx, msg, doff + tail);
else if (dst)
crypto_memzero(dst, dlen);
}
crypto_memzero(tag, sizeof(tag));
if (!ok)
goto bad;
if (slot < 0)
{
*len = mlen;
return 0;
}
rt->slot = (unsigned)slot;
rt->placed = dlen;
*len = doff + tail;
return 0;
bad:
rsmb_err(c, "unseal failed");
return -1;
}
/* rsmb_recv_raw() for a fetch: one message, a placeable READ reply's
* data received straight into its request's place, its signature
* checked over the pieces (*checked set). Sealed messages come back
* unsealed. */
static int rsmb_recv_routed(struct rsmb_ctx *c, size_t *len,
struct rsmb_route *rt, int *checked)
{
uint8_t nb[8];
size_t n, head;
uint32_t doff, dlen;
int slot;
*checked = 0;
rt->slot = rt->n;
/* the length and the first octets of the message, which say
* whether it is sealed: every frame is longer than these */
if (rsmb_recv_bytes(c, nb, 8) != 0)
return -1;
n = ((size_t)nb[1] << 16) | ((size_t)nb[2] << 8) | nb[3];
if (nb[0] != 0 || n < SMB2_HDR_SIZE || n > c->buf_size)
{
rsmb_err(c, "bad frame");
return -1;
}
memcpy(c->rx, nb + 4, 4);
if (c->rx[0] == 0xfd)
{
if (rsmb_recv_sealed(c, n, len, rt) != 0)
return -1;
*checked = rt->slot != rt->n;
return 0;
}
head = n < RSMB_READ_HEAD ? n : RSMB_READ_HEAD;
if (rsmb_recv_bytes(c, c->rx + 4, head - 4) != 0)
return -1;
if (head < n && (slot = rsmb_route_find(rt, c->rx, n, &doff, &dlen)) >= 0)
{
uint8_t *dst = rt->out + rt->offs[slot];
size_t tail = n - doff - dlen;
if ( rsmb_recv_bytes(c, c->rx + head, doff - head) != 0
|| rsmb_recv_bytes(c, dst, dlen) != 0
|| rsmb_recv_bytes(c, c->rx + doff, tail) != 0)
return -1;
if (c->signing && c->session_ready && !c->encrypt)
{
struct rsmb_seg seg[3];
seg[0].p = c->rx; seg[0].len = doff;
seg[1].p = dst; seg[1].len = dlen;
seg[2].p = c->rx + doff; seg[2].len = tail;
if (rsmb_verify_segs(c, c->rx, seg, tail ? 3 : 2) != 0)
{
rsmb_err(c, "bad signature");
return -1;
}
}
*checked = 1;
rt->slot = (unsigned)slot;
rt->placed = dlen;
*len = doff + tail;
return 0;
}
if (rsmb_recv_bytes(c, c->rx + head, n - head) != 0)
return -1;
*len = n;
return 0;
}
/* Receive the next real reply (interim STATUS_PENDING ones are
* skipped) into @c->rx, unsealed and verified; its id in @mid, its
* status in c->status, its credit grant added to the balance. */
static int rsmb_recv_msg(struct rsmb_ctx *c, size_t *rlen, uint64_t *mid)
* status in c->status, its credit grant added to the balance. With
* @rt, a READ reply's data is received where @rt says instead. */
static int rsmb_recv_msg(struct rsmb_ctx *c, size_t *rlen, uint64_t *mid,
struct rsmb_route *rt)
{
int checked = 0;
for (;;)
{
if (rsmb_recv_raw(c, rlen) != 0)
return -1;
if (c->rx[0] == 0xfd)
if (rt)
{
if (!c->encrypt || rsmb_unseal(c, rlen) != 0)
{
rsmb_err(c, "unseal failed");
if (rsmb_recv_routed(c, rlen, rt, &checked) != 0)
return -1;
}
else
{
if (rsmb_recv_raw(c, rlen) != 0)
return -1;
if (c->rx[0] == 0xfd)
{
if (!c->encrypt || rsmb_unseal(c, rlen) != 0)
{
rsmb_err(c, "unseal failed");
return -1;
}
}
}
if (*rlen < SMB2_HDR_SIZE || c->rx[0] != 0xfe)
@@ -554,7 +881,7 @@ static int rsmb_recv_msg(struct rsmb_ctx *c, size_t *rlen, uint64_t *mid)
*mid = smb_get64(c->rx + 24);
c->status = smb_get32(c->rx + 8);
c->credits += (int)smb_get16(c->rx + 14);
if (c->signing && c->session_ready && !c->encrypt
if (c->signing && c->session_ready && !c->encrypt && !checked
&& rsmb_verify_sig(c, c->rx, *rlen) != 0)
{
rsmb_err(c, "bad signature");
@@ -574,7 +901,7 @@ static int rsmb_call(struct rsmb_ctx *c, size_t body_len,
return -1;
do
{
if (rsmb_recv_msg(c, &rlen, &got) != 0)
if (rsmb_recv_msg(c, &rlen, &got, NULL) != 0)
return -1;
} while (got != mid);
if (preauth && c->dialect == DIALECT_311
@@ -1564,12 +1891,19 @@ static int64_t rsmb_fetch(struct rsmb_ctx *c, struct rsmb_file *f,
}
if (!inflight)
break;
/* take one reply and match it */
/* take one reply and match it; a READ's data is received
* straight into its place in @out */
{
size_t rlen;
uint64_t mid;
unsigned slot = RSMB_PIPELINE;
if (rsmb_recv_msg(c, &rlen, &mid) != 0)
size_t rlen;
uint64_t mid;
unsigned slot = RSMB_PIPELINE;
struct rsmb_route rt;
rt.mids = mids;
rt.offs = offs;
rt.lens = lens;
rt.out = out;
rt.n = inflight;
if (rsmb_recv_msg(c, &rlen, &mid, &rt) != 0)
return -1;
for (i = 0; i < inflight; i++)
if (mids[i] == mid)
@@ -1594,14 +1928,17 @@ static int64_t rsmb_fetch(struct rsmb_ctx *c, struct rsmb_file *f,
{
const uint8_t *r = c->rx + SMB2_HDR_SIZE;
uint32_t doff = r[2], got = smb_get32(r + 4);
if (doff < SMB2_HDR_SIZE || doff + got > rlen || got > lens[slot])
if (rt.slot == slot)
got = rt.placed;
else if (doff < SMB2_HDR_SIZE || doff + got > rlen || got > lens[slot])
{
rsmb_err(c, "bad read reply");
fail = 1;
}
else
{
memcpy(out + offs[slot], c->rx + doff, got);
if (!fail)
{
/* a short or empty reply ends the contiguous run
* there, whatever later chunks answered */
if (got < lens[slot])
+39
View File
@@ -0,0 +1,39 @@
include ../../sample_objdir.mk
TARGET := float_to_s16_test
LIBRETRO_COMM_DIR := ../../..
COMMON_SOURCES := \
$(LIBRETRO_COMM_DIR)/audio/conversion/float_to_s16.c \
$(LIBRETRO_COMM_DIR)/features/features_cpu.c \
$(LIBRETRO_COMM_DIR)/compat/compat_strl.c
COMMON_OBJS := $(call sample_objs,$(COMMON_SOURCES:.c=.o))
OBJS := float_to_s16_test.o $(COMMON_OBJS)
CFLAGS += -DFLOAT_TO_S16_TEST_HOOKS -Wall -pedantic -std=gnu99 -g1 -O2 -I$(LIBRETRO_COMM_DIR)/include
LDFLAGS += -lm
ifneq ($(SANITIZER),)
CFLAGS := -fsanitize=$(SANITIZER) -fno-omit-frame-pointer $(CFLAGS)
LDFLAGS := -fsanitize=$(SANITIZER) $(LDFLAGS)
endif
all: $(TARGET)
%.o: %.c
$(CC) -c -o $@ $< $(CFLAGS)
$(SAMPLE_OBJDIR)/%.o: $$(call sample_src,$$*).c
@mkdir -p $(@D)
$(CC) -c -o $@ $< $(CFLAGS)
$(TARGET): $(OBJS)
$(CC) -o $@ $^ $(LDFLAGS)
clean:
rm -rf $(SAMPLE_OBJDIR)
rm -f $(TARGET) $(OBJS)
.PHONY: clean
@@ -0,0 +1,130 @@
/* convert_float_to_s16 against the scalar rule it documents: x * 32768,
* NaN -> 0, round half away from zero, saturate to the s16 range. Every
* tier this CPU has (SSE2, AVX2, AVX-512) runs pinned in turn, on the
* edges above all - exact .5 points after scaling, full scale and past
* it, infinities, NaNs of either sign, denormals - and at every length
* up to 100, so each vector body, the AVX-512 masked tail and the scalar
* remainder are exercised. A guard word after each buffer must survive.
* Every target must give the same s16 for the same float. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <audio/conversion/float_to_s16.h>
#ifdef FLOAT_TO_S16_TEST_HOOKS
extern int float_to_s16_tier_cap;
#endif
static unsigned failures;
static int16_t reference(float v)
{
float scaled = v * 0x8000;
uint32_t bits;
memcpy(&bits, &scaled, sizeof(bits));
if ((bits & 0x7FFFFFFFu) > 0x7F800000u)
return 0;
scaled += (scaled >= 0.0f ? 0.5f : -0.5f);
if (scaled > 32767.0f)
return 0x7FFF;
if (scaled < -32768.0f)
return -0x8000;
return (int16_t)(int32_t)scaled;
}
static float from_bits(uint32_t b)
{
float f;
memcpy(&f, &b, sizeof(f));
return f;
}
static void check(const float *in, size_t n, const char *what, int tier)
{
int16_t *out = (int16_t*)malloc((n + 1) * sizeof(*out));
size_t i;
unsigned bad = 0;
out[n] = 0x5A5A;
convert_float_to_s16(out, in, n);
for (i = 0; i < n; i++)
{
int16_t want = reference(in[i]);
uint32_t bits;
memcpy(&bits, &in[i], sizeof(bits));
if (out[i] != want && bad++ < 4)
printf("FAIL tier %d %s n=%u [%u]: in %.9g (0x%08x) got %d want %d\n",
tier, what, (unsigned)n, (unsigned)i, in[i],
(unsigned)bits, out[i], want);
}
if (out[n] != 0x5A5A)
{
printf("FAIL tier %d %s n=%u: wrote past the end\n", tier, what,
(unsigned)n);
bad++;
}
failures += bad ? 1 : 0;
free(out);
}
int main(void)
{
static const uint32_t special[] = {
0x7FC00000u, 0xFFC00000u, 0x7F800001u, 0xFFFFFFFFu, /* NaNs */
0x7F800000u, 0xFF800000u, /* +-inf */
0x00000001u, 0x80000001u, 0x007FFFFFu, /* denormals */
0x00000000u, 0x80000000u /* +-0 */
};
static const float edges[] = {
1.0f, -1.0f, 1.0000001f, -1.0000001f, 0.99998474f, -0.99998474f,
2.0f, -2.0f, 1e30f, -1e30f,
0.5f / 32768, -0.5f / 32768, 1.5f / 32768, -1.5f / 32768,
2.5f / 32768, -2.5f / 32768, 32766.5f / 32768, -32767.5f / 32768,
32767.5f / 32768, -32768.5f / 32768, 0.49999997f / 32768
};
float *in = (float*)malloc(4096 * sizeof(float));
size_t i, n, k = 0;
uint32_t seed = 0x1234567u;
int tier;
convert_float_to_s16_init_simd();
/* Edges and specials first, then random values of all magnitudes. */
for (i = 0; i < sizeof(special) / sizeof(special[0]); i++)
in[k++] = from_bits(special[i]);
for (i = 0; i < sizeof(edges) / sizeof(edges[0]); i++)
in[k++] = edges[i];
for (; k < 4096; k++)
{
seed = seed * 1664525u + 1013904223u;
in[k] = (k & 1) ? from_bits(seed) /* any bits */
: (float)((int32_t)seed) / 2147483648.0f * 1.25f; /* audio */
}
for (tier = 0; tier <= 2; tier++)
{
#ifdef FLOAT_TO_S16_TEST_HOOKS
float_to_s16_tier_cap = tier;
#else
if (tier)
break;
#endif
check(in, 4096, "corpus", tier);
for (n = 0; n <= 100; n++)
{
check(in, n, "length", tier);
check(in + 1 + (n % 7), n, "offset", tier);
}
}
free(in);
if (failures)
{
printf("float_to_s16_test: %u failing cases\n", failures);
return 1;
}
printf("float_to_s16_test: every tier matches the scalar rule\n");
return 0;
}
+39
View File
@@ -0,0 +1,39 @@
include ../../sample_objdir.mk
TARGET := s16_to_float_test
LIBRETRO_COMM_DIR := ../../..
COMMON_SOURCES := \
$(LIBRETRO_COMM_DIR)/audio/conversion/s16_to_float.c \
$(LIBRETRO_COMM_DIR)/features/features_cpu.c \
$(LIBRETRO_COMM_DIR)/compat/compat_strl.c
COMMON_OBJS := $(call sample_objs,$(COMMON_SOURCES:.c=.o))
OBJS := s16_to_float_test.o $(COMMON_OBJS)
CFLAGS += -DS16_TO_FLOAT_TEST_HOOKS -Wall -pedantic -std=gnu99 -g1 -O2 -I$(LIBRETRO_COMM_DIR)/include
LDFLAGS += -lm
ifneq ($(SANITIZER),)
CFLAGS := -fsanitize=$(SANITIZER) -fno-omit-frame-pointer $(CFLAGS)
LDFLAGS := -fsanitize=$(SANITIZER) $(LDFLAGS)
endif
all: $(TARGET)
%.o: %.c
$(CC) -c -o $@ $< $(CFLAGS)
$(SAMPLE_OBJDIR)/%.o: $$(call sample_src,$$*).c
@mkdir -p $(@D)
$(CC) -c -o $@ $< $(CFLAGS)
$(TARGET): $(OBJS)
$(CC) -o $@ $^ $(LDFLAGS)
clean:
rm -rf $(SAMPLE_OBJDIR)
rm -f $(TARGET) $(OBJS)
.PHONY: clean
@@ -0,0 +1,87 @@
/* convert_s16_to_float against the scalar rule, (float)x * (gain /
* 32768), bit for bit: every s16 value at several gains, then every
* length up to 100 at odd offsets so each vector body, the AVX-512
* masked tail and the scalar remainder run, with a guard word after
* each buffer. Every tier the CPU has (SSE2, AVX2, AVX-512) is pinned
* in turn; all must give the same float for the same sample. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <audio/conversion/s16_to_float.h>
#ifdef S16_TO_FLOAT_TEST_HOOKS
extern int s16_to_float_tier_cap;
#endif
static unsigned failures;
static void check(const int16_t *in, size_t n, float gain,
const char *what, int tier)
{
float *out = (float*)malloc((n + 1) * sizeof(*out));
float g = gain / 0x8000;
uint32_t guard = 0x5A5A5A5Au;
size_t i;
unsigned bad = 0;
memcpy(&out[n], &guard, sizeof(guard));
convert_s16_to_float(out, in, n, gain);
for (i = 0; i < n; i++)
{
float want = (float)in[i] * g;
if (memcmp(&out[i], &want, sizeof(want)) && bad++ < 4)
printf("FAIL tier %d %s gain %g n=%u [%u]: in %d got %.9g want %.9g\n",
tier, what, gain, (unsigned)n, (unsigned)i, in[i],
out[i], want);
}
if (memcmp(&out[n], &guard, sizeof(guard)))
{
printf("FAIL tier %d %s n=%u: wrote past the end\n", tier, what,
(unsigned)n);
bad++;
}
failures += bad ? 1 : 0;
free(out);
}
int main(void)
{
static const float gains[] = { 1.0f, 0.5f, 0.0f, 1.37f, 3.98107f,
0.000123f };
int16_t *in = (int16_t*)malloc(65536 * sizeof(int16_t));
size_t i, n, g;
int tier;
for (i = 0; i < 65536; i++)
in[i] = (int16_t)(uint16_t)(i * 40503u);
convert_s16_to_float_init_simd();
for (tier = 0; tier <= 2; tier++)
{
#ifdef S16_TO_FLOAT_TEST_HOOKS
s16_to_float_tier_cap = tier;
#else
if (tier)
break;
#endif
for (g = 0; g < sizeof(gains) / sizeof(gains[0]); g++)
check(in, 65536, gains[g], "every value", tier);
for (n = 0; n <= 100; n++)
{
check(in, n, 1.0f, "length", tier);
check(in + 1 + (n % 7), n, 0.75f, "offset", tier);
}
}
free(in);
if (failures)
{
printf("s16_to_float_test: %u failing cases\n", failures);
return 1;
}
printf("s16_to_float_test: every tier matches the scalar rule\n");
return 0;
}
+118 -3
View File
@@ -277,14 +277,18 @@ static void length_alignment_sweep(void)
static void fold_boundaries(void)
{
/* +/-1 around every size where an implementation changes gear: the
* 64-byte four-way fold, the 16-byte single fold, and page-sized
* multiples for good measure. */
* 64-byte four-way fold, the 16-byte single fold, the 256-octet
* 512-bit fold and the 64-octet one after it, ARM's three lanes of
* 1 KiB, and page-sized multiples for good measure. */
static const size_t sizes[] = {
1, 2, 3, 7, 8, 9, 15, 16, 17,
31, 32, 33, 47, 48, 49, 62, 63, 64, 65, 66,
79, 80, 81, 95, 96, 97, 111, 112, 113, 127, 128, 129,
143, 144, 145, 175, 176, 191, 192, 193, 255, 256, 257,
511, 512, 513, 1023, 1024, 1025, 4095, 4096, 4097,
319, 320, 321, 383, 384, 385, 447, 448, 449,
511, 512, 513, 767, 768, 769, 1023, 1024, 1025,
1279, 1280, 1281, 2047, 2048, 2049, 3071, 3072, 3073, 3079,
4095, 4096, 4097, 6143, 6144, 6145, 9216, 9217,
65535, 65536, 65537
};
unsigned k;
@@ -668,6 +672,116 @@ static void concurrent_stress(void)
/* ------------------------------------------------------------------ */
/* ------------------------------------------------------------------ */
/* 8. The MSB-first CRCs: Ogg's CRC-32 and CRC-16/CCITT */
/* ------------------------------------------------------------------ */
/* Bit by bit, from the polynomials as they are written */
static uint32_t ogg_bitwise(uint32_t crc, const uint8_t *data, size_t len)
{
size_t i;
int b;
for (i = 0; i < len; i++)
{
crc ^= (uint32_t)data[i] << 24;
for (b = 0; b < 8; b++)
crc = (crc & 0x80000000U) ? ((crc << 1) ^ 0x04C11DB7U) : (crc << 1);
}
return crc;
}
static uint16_t ccitt_bitwise(uint16_t crc, const uint8_t *data, size_t len)
{
size_t i;
int b;
for (i = 0; i < len; i++)
{
crc ^= (uint16_t)(data[i] << 8);
for (b = 0; b < 8; b++)
crc = (uint16_t)((crc & 0x8000) ? ((crc << 1) ^ 0x1021) : (crc << 1));
}
return crc;
}
/* Every length to 1200 at 16 alignments, from two seeds; the folds'
* seams; and a 64 KiB buffer split anywhere */
static void msb_crcs(void)
{
static const size_t seams[] = { 63, 64, 65, 79, 80, 127, 128, 129,
255, 256, 257, 319, 320, 321, 511, 512, 513, 767, 768, 769, 1023,
1024, 1025, 2047, 2048, 2049, 4095, 4096, 4097, 65535, 65536 };
size_t big = 65536 + 16, n, off, k;
uint8_t *m = (uint8_t *)malloc(big);
long cases = 0;
int bad = 0;
if (!m)
{
printf("[FAIL] %-26s out of memory\n", "MSB-first CRCs");
failures++;
return;
}
for (n = 0; n < big; n++)
m[n] = (uint8_t)(n * 2654435761U >> 13);
for (n = 0; n <= SWEEP_MAX && bad < 8; n++)
for (off = 0; off < 16; off++)
{
uint32_t s32 = (n & 1) ? 0xFFFFFFFFU : 0x1234ABCDU;
uint16_t s16 = (n & 1) ? 0xFFFF : 0x1D0F;
if (encoding_crc32_ogg(0, m + off, n) != ogg_bitwise(0, m + off, n)
|| encoding_crc32_ogg(s32, m + off, n)
!= ogg_bitwise(s32, m + off, n)
|| encoding_crc16_ccitt(s16, m + off, n)
!= ccitt_bitwise(s16, m + off, n)
|| encoding_crc16_ccitt(0, m + off, n)
!= ccitt_bitwise(0, m + off, n))
{
printf("[FAIL] %-26s len=%lu align=%lu\n", "MSB-first CRCs",
(unsigned long)n, (unsigned long)off);
bad++;
}
cases += 4;
}
for (k = 0; k < sizeof(seams) / sizeof(seams[0]) && bad < 8; k++)
for (off = 0; off < 16; off++)
{
n = seams[k];
if ( encoding_crc32_ogg(0x5A5A5A5AU, m + off, n)
!= ogg_bitwise(0x5A5A5A5AU, m + off, n)
|| encoding_crc16_ccitt(0xFFFF, m + off, n)
!= ccitt_bitwise(0xFFFF, m + off, n))
{
printf("[FAIL] %-26s seam len=%lu align=%lu\n",
"MSB-first CRCs", (unsigned long)n, (unsigned long)off);
bad++;
}
cases += 2;
}
{
uint32_t want32 = ogg_bitwise(0, m, 65536);
uint16_t want16 = ccitt_bitwise(0xFFFF, m, 65536);
for (k = 0; k <= 65536 && bad < 8; k += 1 + (k >> 4))
{
if ( encoding_crc32_ogg(encoding_crc32_ogg(0, m, k), m + k,
65536 - k) != want32
|| encoding_crc16_ccitt(encoding_crc16_ccitt(0xFFFF, m, k),
m + k, 65536 - k) != want16)
{
printf("[FAIL] %-26s split at %lu\n", "MSB-first CRCs",
(unsigned long)k);
bad++;
}
cases += 2;
}
}
free(m);
failures += bad;
if (!bad)
printf("[ok] %-26s %ld cases, Ogg's and CCITT's\n",
"MSB-first CRCs", cases);
}
int main(void)
{
/* The concurrency check runs FIRST and must stay first. Its whole
@@ -685,6 +799,7 @@ int main(void)
cross_path_agreement();
guard_page();
concurrent_stress();
msb_crcs();
printf("%s\n", failures ? "FAILED" : "PASS");
return failures ? 1 : 0;
+39
View File
@@ -0,0 +1,39 @@
include ../../sample_objdir.mk
TARGET := rbzip2_test
LIBRETRO_COMM_DIR := ../../..
SOURCES := \
rbzip2_test.c \
$(LIBRETRO_COMM_DIR)/encodings/encoding_rbzip2.c
CFLAGS += -Wall -pedantic -std=gnu99 -g1 -O2 -I$(LIBRETRO_COMM_DIR)/include
OBJS := $(call sample_objs,$(SOURCES:.c=.o))
ifneq ($(SANITIZER),)
CFLAGS := -fsanitize=$(SANITIZER) -fno-omit-frame-pointer $(CFLAGS)
LDFLAGS := -fsanitize=$(SANITIZER) $(LDFLAGS)
endif
all: $(TARGET)
%.o: %.c bz_write.h rbzip2_fixtures.h
$(CC) -c -o $@ $< $(CFLAGS)
$(SAMPLE_OBJDIR)/%.o: $$(call sample_src,$$*).c
@mkdir -p $(@D)
$(CC) -c -o $@ $< $(CFLAGS)
$(TARGET): $(OBJS)
$(CC) -o $@ $^ $(LDFLAGS)
check: $(TARGET)
./$(TARGET)
clean:
rm -rf $(SAMPLE_OBJDIR)
rm -f $(TARGET) $(OBJS)
.PHONY: all check clean
+339
View File
@@ -0,0 +1,339 @@
/* A small bzip2 writer for the tests: real streams, simply made. The
* transform is a sort of the block's rotations by prefix doubling,
* the tables two to six, one sized to the symbols' frequencies and
* the others plain or lengthened from it, chosen for each fifty
* symbols by a seeded generator so every selector shape is written.
* Not for production: it is slow, and its ratio is not bzip2's. */
#ifndef BZ_WRITE_H
#define BZ_WRITE_H
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
/* nonzero: each block's CRC written wrong, and the stream's from those */
static int bzw_lie;
struct bzw
{
uint8_t *out;
size_t cap;
size_t len;
uint64_t acc;
unsigned nb;
uint32_t crc[256];
uint32_t seed;
int fail;
};
static void bzw_bits(struct bzw *w, uint32_t v, unsigned k)
{
w->acc = (w->acc << k) | (v & ((1u << k) - 1));
w->nb += k;
while (w->nb >= 8)
{
if (w->len < w->cap)
w->out[w->len] = (uint8_t)(w->acc >> (w->nb - 8));
else
w->fail = 1;
w->len++;
w->nb -= 8;
}
}
static uint32_t bzw_rand(struct bzw *w)
{
w->seed = w->seed * 1103515245u + 12345u;
return w->seed >> 8;
}
/* the rotations' order, by prefix doubling */
static const uint32_t *bzw_rank;
static uint32_t bzw_n, bzw_k;
static int bzw_cmp(const void *a, const void *b)
{
uint32_t i = *(const uint32_t*)a, j = *(const uint32_t*)b;
if (bzw_rank[i] != bzw_rank[j])
return bzw_rank[i] < bzw_rank[j] ? -1 : 1;
i = (i + bzw_k) % bzw_n;
j = (j + bzw_k) % bzw_n;
if (bzw_rank[i] != bzw_rank[j])
return bzw_rank[i] < bzw_rank[j] ? -1 : 1;
return 0;
}
static void bzw_sort(const uint8_t *blk, uint32_t n, uint32_t *sa)
{
uint32_t *rank = (uint32_t*)malloc(n * sizeof(uint32_t));
uint32_t *next = (uint32_t*)malloc(n * sizeof(uint32_t));
uint32_t i, k;
for (i = 0; i < n; i++)
{
sa[i] = i;
rank[i] = blk[i];
}
bzw_rank = rank;
bzw_n = n;
for (k = 1; ; k <<= 1)
{
bzw_k = k;
qsort(sa, n, sizeof(uint32_t), bzw_cmp);
next[sa[0]] = 0;
for (i = 1; i < n; i++)
next[sa[i]] = next[sa[i - 1]] + (bzw_cmp(&sa[i - 1], &sa[i]) != 0);
memcpy(rank, next, n * sizeof(uint32_t));
if (rank[sa[n - 1]] == n - 1 || k >= n)
break;
}
free(rank);
free(next);
}
static uint32_t bzw_crc(const struct bzw *w, uint32_t v, const uint8_t *p,
size_t n)
{
for (; n; n--, p++)
v = (v << 8) ^ w->crc[(v >> 24) ^ *p];
return v;
}
/* @z ranks of 0 as RUNA (0) and RUNB (1), bijective base 2 */
static void bzw_zrun(uint16_t *syms, uint32_t *nsym, uint32_t z)
{
if (!z)
return;
z--;
for (;;)
{
syms[(*nsym)++] = (uint16_t)(z & 1);
if (z < 2)
break;
z = (z - 2) / 2;
}
}
/* One block of the run-coded @blk (@n octets), which stands for
* octets whose CRC is @crc. */
static void bzw_block(struct bzw *w, const uint8_t *blk, uint32_t n,
uint32_t crc)
{
uint32_t *sa = (uint32_t*)malloc(n * sizeof(uint32_t));
uint16_t *syms = (uint16_t*)malloc((n + 1) * sizeof(uint16_t));
uint8_t used[256], mtf[256], len[6][258], sel_pos[6];
uint32_t freq[258];
uint32_t i, j, orig = 0, nsym = 0, zrun = 0, n_in_use = 0, alpha;
uint32_t n_groups, n_sel, flat;
bzw_sort(blk, n, sa);
memset(used, 0, sizeof(used));
for (i = 0; i < n; i++)
used[blk[i]] = 1;
for (i = 0; i < 256; i++)
if (used[i])
mtf[n_in_use++] = (uint8_t)i;
alpha = n_in_use + 2;
/* the last column, move-to-front, runs of rank 0 in base 2 */
memset(freq, 0, sizeof(freq));
for (i = 0; i < n; i++)
{
uint8_t c = blk[(sa[i] + n - 1) % n];
uint32_t r = 0;
if (sa[i] == 0)
orig = i;
while (mtf[r] != c)
r++;
if (!r)
{
zrun++;
continue;
}
bzw_zrun(syms, &nsym, zrun);
zrun = 0;
memmove(mtf + 1, mtf, r);
mtf[0] = c;
syms[nsym++] = (uint16_t)(r + 1);
}
bzw_zrun(syms, &nsym, zrun);
syms[nsym++] = (uint16_t)(n_in_use + 1);
for (i = 0; i < nsym; i++)
freq[syms[i]]++;
/* table 0 from the frequencies, kept to Kraft's inequality */
for (j = 0; j < alpha; j++)
{
uint32_t f = freq[j] + 1, l = 1;
while (l < 17 && ((uint64_t)f << l) < (uint64_t)nsym + alpha)
l++;
len[0][j] = (uint8_t)l;
}
for (;;)
{
uint64_t kraft = 0;
uint32_t worst = alpha;
for (j = 0; j < alpha; j++)
kraft += (uint64_t)1 << (17 - len[0][j]);
if (kraft <= ((uint64_t)1 << 17))
break;
for (j = 0; j < alpha; j++)
if (len[0][j] < 17 && (worst == alpha || freq[j] < freq[worst]))
worst = j;
len[0][worst]++;
}
/* table 1 plain, the others table 0 or 1 lengthened */
for (flat = 1; (1u << flat) < alpha; flat++) { }
n_groups = 2 + bzw_rand(w) % 5;
for (j = 0; j < alpha; j++)
{
len[1][j] = (uint8_t)flat;
for (i = 2; i < n_groups; i++)
{
uint32_t l = len[i & 1][j] + (bzw_rand(w) % 3);
len[i][j] = (uint8_t)(l > 20 ? 20 : l);
}
}
n_sel = (nsym + 49) / 50;
bzw_bits(w, 0x314159, 24);
bzw_bits(w, 0x265359, 24);
bzw_bits(w, crc >> 16, 16);
bzw_bits(w, crc, 16);
bzw_bits(w, 0, 1);
bzw_bits(w, orig, 24);
{
uint32_t m = 0;
for (i = 0; i < 16; i++)
for (j = 0; j < 16; j++)
if (used[i * 16 + j])
{
m |= 0x8000u >> i;
break;
}
bzw_bits(w, m, 16);
for (i = 0; i < 16; i++)
if (m & (0x8000u >> i))
{
uint32_t b = 0;
for (j = 0; j < 16; j++)
if (used[i * 16 + j])
b |= 0x8000u >> j;
bzw_bits(w, b, 16);
}
}
bzw_bits(w, n_groups, 3);
bzw_bits(w, n_sel, 15);
{
uint8_t *sel = (uint8_t*)malloc(n_sel);
for (i = 0; i < n_groups; i++)
sel_pos[i] = (uint8_t)i;
for (i = 0; i < n_sel; i++)
{
uint8_t t = (uint8_t)(bzw_rand(w) % 4 ? (bzw_rand(w) & 1)
: bzw_rand(w) % n_groups);
sel[i] = t;
for (j = 0; sel_pos[j] != t; j++)
bzw_bits(w, 1, 1);
bzw_bits(w, 0, 1);
memmove(sel_pos + 1, sel_pos, j);
sel_pos[0] = t;
}
for (i = 0; i < n_groups; i++)
{
uint32_t cur = len[i][0];
bzw_bits(w, cur, 5);
for (j = 0; j < alpha; j++)
{
while (cur < len[i][j]) { bzw_bits(w, 2, 2); cur++; }
while (cur > len[i][j]) { bzw_bits(w, 3, 2); cur--; }
bzw_bits(w, 0, 1);
}
}
/* canonical codes, as the reader assigns them */
{
uint32_t code[6][258];
for (i = 0; i < n_groups; i++)
{
uint32_t c = 0, l;
for (l = 1; l <= 20; l++)
{
for (j = 0; j < alpha; j++)
if (len[i][j] == l)
code[i][j] = c++;
c <<= 1;
}
}
for (i = 0; i < nsym; i++)
{
uint32_t t = sel[i / 50];
bzw_bits(w, code[t][syms[i]], len[t][syms[i]]);
}
}
free(sel);
}
free(sa);
free(syms);
}
/* @src (@n octets) as one stream at block size @level (1-9) into @out;
* returns the stream's length, 0 when @cap is short. */
static size_t bz_write(uint8_t *out, size_t cap, const uint8_t *src,
size_t n, unsigned level, uint32_t seed)
{
struct bzw w;
uint32_t max = 100000u * level - 19, combined = 0, i, j;
uint8_t *blk = (uint8_t*)malloc(max + 5);
size_t at = 0;
memset(&w, 0, sizeof(w));
w.out = out;
w.cap = cap;
w.seed = seed;
for (i = 0; i < 256; i++)
{
uint32_t c = i << 24;
for (j = 0; j < 8; j++)
c = (c & 0x80000000u) ? (c << 1) ^ 0x04c11db7u : c << 1;
w.crc[i] = c;
}
bzw_bits(&w, 'B', 8);
bzw_bits(&w, 'Z', 8);
bzw_bits(&w, 'h', 8);
bzw_bits(&w, '0' + level, 8);
while (at < n)
{
uint32_t bn = 0, crc;
size_t from = at;
/* runs of four or more as four and a count, never split */
while (at < n && bn + 5 <= max)
{
size_t r = 1;
while (at + r < n && src[at + r] == src[at] && r < 255)
r++;
if (r < 4)
{
blk[bn++] = src[at++];
continue;
}
memset(blk + bn, src[at], 4);
blk[bn + 4] = (uint8_t)(r - 4);
bn += 5;
at += r;
}
crc = ~bzw_crc(&w, 0xffffffffu, src + from, at - from)
^ (bzw_lie ? 1u : 0u);
combined = ((combined << 1) | (combined >> 31)) ^ crc;
bzw_block(&w, blk, bn, crc);
}
bzw_bits(&w, 0x177245, 24);
bzw_bits(&w, 0x385090, 24);
bzw_bits(&w, combined >> 16, 16);
bzw_bits(&w, combined, 16);
if (w.nb)
bzw_bits(&w, 0, 8 - w.nb);
free(blk);
return w.fail ? 0 : w.len;
}
#endif
+365
View File
@@ -0,0 +1,365 @@
/* Streams written by bzip2 1.0.8 (libbz2) from the generators in
* rbzip2_test.c: A at block size 9, B at 1, C at 2. */
static const uint8_t fx_a[1608] = {
0x42, 0x5a, 0x68, 0x39, 0x31, 0x41, 0x59, 0x26, 0x53, 0x59, 0x19, 0x92,
0xac, 0xd8, 0x00, 0x02, 0x81, 0xc1, 0x80, 0x40, 0x00, 0x3f, 0xff, 0xc0,
0x00, 0x60, 0x07, 0x93, 0xb6, 0xba, 0xd5, 0xcb, 0xbb, 0x4d, 0xee, 0x7b,
0xe5, 0xf7, 0x7b, 0x67, 0xb7, 0x6e, 0xf7, 0xb7, 0x4d, 0xb7, 0x9c, 0x7a,
0xea, 0xdb, 0xc4, 0x53, 0xc0, 0x04, 0xc5, 0x41, 0x15, 0x3f, 0x00, 0x02,
0x48, 0x55, 0x3f, 0x4d, 0x34, 0xd9, 0x02, 0x53, 0x15, 0x4f, 0x7a, 0x4c,
0xd0, 0x11, 0x4c, 0x45, 0x53, 0xfd, 0x36, 0xa9, 0xe9, 0x9a, 0x13, 0x55,
0x31, 0x15, 0x4f, 0xfd, 0x00, 0x49, 0xe1, 0x54, 0xd7, 0x43, 0xe5, 0xa9,
0x18, 0x3a, 0x11, 0x09, 0x0b, 0x45, 0xf7, 0x31, 0xd3, 0x0c, 0xd3, 0xa9,
0xf9, 0xbe, 0xbf, 0xb1, 0xc0, 0x7a, 0x2b, 0x29, 0x28, 0xd8, 0x82, 0xc1,
0x4f, 0x0b, 0x63, 0x4a, 0x47, 0x62, 0x93, 0xe9, 0x88, 0x44, 0x92, 0xbb,
0x25, 0x64, 0xc0, 0xa2, 0x95, 0xe6, 0xf0, 0xef, 0x6a, 0xf3, 0xce, 0x13,
0xed, 0x31, 0xd3, 0x3d, 0xe4, 0x45, 0x0d, 0xd9, 0x7c, 0xe3, 0x60, 0x22,
0xf1, 0x96, 0x7c, 0xd7, 0xbe, 0x91, 0xab, 0xa1, 0x74, 0xb7, 0xd5, 0xc8,
0x71, 0x4b, 0x15, 0x4a, 0xcd, 0xf7, 0x45, 0xc5, 0xdb, 0x4f, 0x95, 0xec,
0xfd, 0x3c, 0x2c, 0xfa, 0x23, 0xab, 0x39, 0x74, 0xe5, 0xb6, 0xb0, 0xd5,
0xd9, 0x50, 0xd2, 0x10, 0x8e, 0x9d, 0xd2, 0x5d, 0x74, 0x02, 0x7d, 0x6c,
0xfc, 0x8f, 0x3e, 0x75, 0x3c, 0x41, 0x3a, 0x39, 0x01, 0x63, 0x56, 0x7e,
0x37, 0xc9, 0x28, 0xef, 0x4e, 0x6e, 0xaa, 0x74, 0x7b, 0x91, 0xac, 0xae,
0x5d, 0xd9, 0x0c, 0x27, 0xee, 0xf7, 0x5e, 0x0e, 0x3b, 0xa5, 0x18, 0x0e,
0x29, 0x1d, 0x10, 0x9b, 0xf4, 0x49, 0x30, 0xc3, 0x60, 0x39, 0xe3, 0xbb,
0x5d, 0x85, 0x2d, 0x61, 0x5b, 0xda, 0xc7, 0xb9, 0xec, 0xa5, 0xb7, 0xb0,
0xc1, 0xc9, 0xaa, 0x23, 0x22, 0x93, 0x31, 0x4c, 0x08, 0x73, 0x53, 0xd7,
0xbe, 0x58, 0x9d, 0xdf, 0x5e, 0x40, 0x2c, 0xd0, 0xcf, 0x01, 0x1b, 0x72,
0x1c, 0xd7, 0x66, 0xc8, 0xbb, 0x58, 0x0b, 0xe2, 0xe9, 0xec, 0xb8, 0xeb,
0xe1, 0x20, 0xc0, 0x2d, 0xc4, 0x9f, 0xee, 0xa4, 0x6e, 0x51, 0x5a, 0xcf,
0x1d, 0x05, 0x79, 0x78, 0x05, 0xb0, 0xb6, 0x96, 0xb7, 0xcf, 0x8c, 0xe0,
0x2e, 0x21, 0x61, 0x34, 0xd8, 0x8f, 0xa5, 0x3a, 0x41, 0x5b, 0x7c, 0xd3,
0x7b, 0x19, 0x52, 0xae, 0xa2, 0xba, 0x3d, 0xc4, 0x7d, 0x64, 0x59, 0x56,
0x1b, 0x27, 0x49, 0xbd, 0xb2, 0xaa, 0xdc, 0xb3, 0x67, 0x56, 0xa1, 0x56,
0xa1, 0x2a, 0x86, 0x32, 0x95, 0x8f, 0xe1, 0x8a, 0x5d, 0x7c, 0x7a, 0xf2,
0x66, 0x2d, 0x30, 0x25, 0x83, 0x6f, 0x82, 0xf3, 0x8f, 0x76, 0xa4, 0xc4,
0xd1, 0x0e, 0x13, 0x3b, 0x3d, 0x8c, 0x69, 0xa8, 0xd4, 0x11, 0xa1, 0xae,
0x8c, 0x12, 0xa6, 0x68, 0x1b, 0xbd, 0x32, 0x33, 0xf9, 0x7c, 0xba, 0x46,
0x48, 0xdd, 0x4d, 0xa8, 0x67, 0x78, 0x3c, 0xb6, 0x73, 0xc9, 0x9b, 0xb4,
0x9f, 0x6e, 0x9b, 0x15, 0xc8, 0x18, 0x6a, 0xbe, 0x2b, 0x59, 0x50, 0xf1,
0xf9, 0x0d, 0xe4, 0x6d, 0xa6, 0xab, 0x0d, 0x63, 0x6e, 0xfb, 0x55, 0x72,
0x5f, 0x8b, 0xf0, 0x3d, 0xa6, 0x37, 0x21, 0xa9, 0xfd, 0x39, 0xa3, 0x67,
0x34, 0x38, 0x3b, 0xa9, 0x2c, 0xac, 0x53, 0x90, 0x85, 0x6b, 0x47, 0x00,
0x4d, 0x95, 0xc5, 0x9e, 0x19, 0xbc, 0xfa, 0x6a, 0xdb, 0x43, 0x5f, 0x97,
0x2b, 0x5d, 0x50, 0xd8, 0x81, 0x86, 0x97, 0xba, 0xaf, 0xda, 0x54, 0x48,
0x87, 0x0e, 0xf0, 0x35, 0xab, 0x39, 0x98, 0xd7, 0x73, 0xa3, 0xed, 0x0a,
0x8b, 0xd6, 0x38, 0xce, 0x13, 0xde, 0xb3, 0x4b, 0x70, 0x23, 0xf6, 0xda,
0x9c, 0x6c, 0x16, 0xcb, 0x9e, 0x10, 0x4d, 0x87, 0x08, 0xc0, 0x9b, 0xbf,
0xd4, 0x95, 0x37, 0x2e, 0x7a, 0xee, 0xd5, 0x0f, 0x85, 0xed, 0x5c, 0x5b,
0xa3, 0x2d, 0x57, 0x8a, 0x99, 0x05, 0xe9, 0xdc, 0xe1, 0xae, 0xeb, 0x78,
0x89, 0xac, 0x6f, 0x16, 0x58, 0x1b, 0xcb, 0x66, 0x13, 0x68, 0x3e, 0xa2,
0xb8, 0xd3, 0xb8, 0x26, 0x69, 0xba, 0x70, 0xa9, 0x06, 0x7b, 0xb1, 0x5b,
0x61, 0xd0, 0x26, 0x7d, 0xd6, 0x9c, 0x30, 0xb4, 0xd6, 0x23, 0x7d, 0x0a,
0x6f, 0x11, 0xf0, 0xca, 0x6b, 0x75, 0xe3, 0x3a, 0xa8, 0xce, 0x45, 0x52,
0x04, 0x7d, 0xdb, 0xca, 0xf7, 0x9f, 0xd7, 0x3c, 0xdb, 0xc3, 0x09, 0x64,
0x83, 0x07, 0x31, 0xb4, 0x81, 0x86, 0x33, 0xaa, 0x94, 0x88, 0x12, 0x6d,
0x82, 0x0b, 0x04, 0xd7, 0xc6, 0xd4, 0xf0, 0x2b, 0x4e, 0x2b, 0xc5, 0x03,
0x74, 0x99, 0x03, 0x62, 0x63, 0x5e, 0x35, 0x52, 0x3d, 0xd9, 0xdd, 0x9a,
0x91, 0x80, 0x24, 0x31, 0xca, 0xda, 0x45, 0x38, 0xbb, 0xd9, 0x7b, 0x0f,
0x3d, 0x51, 0x73, 0x81, 0xa2, 0x6e, 0x18, 0xcb, 0x3a, 0x80, 0x9b, 0x35,
0x01, 0xbe, 0x1e, 0x6e, 0xd6, 0x35, 0x56, 0xea, 0xe9, 0x27, 0x06, 0x32,
0x38, 0x68, 0x13, 0x82, 0x9b, 0x56, 0x94, 0x58, 0xbe, 0xb9, 0x10, 0x96,
0x69, 0x65, 0x35, 0x36, 0x6a, 0x7b, 0x79, 0x6c, 0x79, 0x58, 0x46, 0x94,
0xf6, 0xb9, 0x02, 0xca, 0x2a, 0x63, 0xb4, 0x4b, 0x5a, 0xaf, 0x14, 0xdf,
0x44, 0x52, 0xaf, 0xcd, 0x9e, 0xe3, 0x20, 0x2f, 0x31, 0x17, 0xe8, 0xf5,
0xc1, 0x1c, 0x81, 0x17, 0x9a, 0x09, 0xda, 0x49, 0x18, 0xdc, 0x9d, 0x03,
0x24, 0x9e, 0x66, 0x68, 0xf8, 0x09, 0xf4, 0x0d, 0x5b, 0xf5, 0x4b, 0x44,
0x36, 0x65, 0x55, 0x43, 0xdf, 0x6d, 0x77, 0x80, 0x37, 0xa4, 0x99, 0xae,
0x91, 0xbf, 0xde, 0xe3, 0xa3, 0xe9, 0xe5, 0x78, 0xb1, 0xda, 0xd4, 0xe5,
0x3c, 0x34, 0x6a, 0x1c, 0xd1, 0xd0, 0x33, 0x5e, 0x0f, 0x5d, 0xd7, 0x5a,
0xd1, 0xd1, 0x30, 0x88, 0x11, 0x00, 0xa1, 0x8c, 0xa8, 0xdd, 0xeb, 0x79,
0xec, 0xdb, 0x16, 0xa0, 0x09, 0x02, 0x84, 0xfc, 0x18, 0x02, 0xc4, 0x60,
0x57, 0xaa, 0x18, 0xf2, 0x0c, 0x7e, 0x46, 0x74, 0x1d, 0x15, 0xb7, 0x33,
0x99, 0xdd, 0xaf, 0xb2, 0x5f, 0xaa, 0xf5, 0x1c, 0x94, 0x18, 0x05, 0xd6,
0x8c, 0x32, 0x60, 0xb1, 0xee, 0x3a, 0x6f, 0x70, 0xa2, 0x07, 0x88, 0x96,
0x0b, 0x1f, 0x03, 0x55, 0x57, 0x6f, 0x0d, 0x13, 0x1b, 0x28, 0xa6, 0xb2,
0xed, 0xd3, 0xbd, 0xa1, 0xd9, 0x84, 0x03, 0xb1, 0x63, 0xca, 0xa6, 0x94,
0xf0, 0x03, 0xc3, 0x77, 0xc4, 0x79, 0xb9, 0x6f, 0x1c, 0x17, 0x0d, 0x8c,
0xd1, 0x38, 0x90, 0x8c, 0x4e, 0x47, 0x10, 0x9e, 0x0f, 0x4b, 0x66, 0x70,
0x82, 0xe6, 0x35, 0xac, 0x5d, 0xd1, 0x61, 0x1f, 0x74, 0x2a, 0x3e, 0xf9,
0x14, 0xb2, 0xb3, 0x2a, 0xbd, 0xb4, 0x84, 0x6a, 0x05, 0xe5, 0x75, 0x3d,
0xc3, 0x61, 0xc7, 0x2e, 0xa9, 0xa8, 0x58, 0xed, 0x0e, 0xde, 0xb4, 0x7b,
0x45, 0xb1, 0x4e, 0x1f, 0x6c, 0xb8, 0xdd, 0xd2, 0x29, 0xaf, 0xcd, 0xe9,
0x85, 0xb8, 0xe6, 0x77, 0xe6, 0x06, 0x3c, 0x0d, 0xb0, 0x71, 0xf5, 0xce,
0x1c, 0x74, 0xa9, 0xcd, 0xc3, 0x63, 0x36, 0x8e, 0x87, 0x43, 0xcf, 0x77,
0xce, 0x8f, 0xac, 0xb6, 0xe9, 0x94, 0x56, 0x79, 0x82, 0x40, 0xf4, 0x95,
0x46, 0x25, 0xeb, 0xcd, 0x3e, 0xae, 0xc8, 0xc2, 0xca, 0x5b, 0xa2, 0xef,
0xb1, 0xe2, 0x7e, 0x7b, 0x75, 0xf8, 0x9d, 0x71, 0x03, 0x7b, 0x9d, 0xc2,
0xb8, 0xce, 0x02, 0x70, 0x8c, 0x1a, 0x2b, 0xf1, 0xed, 0x63, 0x24, 0x08,
0x20, 0xfc, 0xe1, 0xf7, 0x79, 0x9b, 0xdd, 0xde, 0xe1, 0x17, 0xcb, 0xe3,
0x20, 0xf3, 0x3b, 0x52, 0x02, 0x7c, 0x71, 0x6a, 0x54, 0x6f, 0xf5, 0x96,
0x2b, 0xd3, 0x7d, 0x31, 0x0a, 0xb7, 0x42, 0x4c, 0x10, 0x7a, 0xfc, 0x1e,
0x99, 0xbd, 0xc2, 0xdb, 0x7b, 0xb5, 0x71, 0x69, 0xd0, 0x8b, 0x9a, 0x2d,
0xc8, 0xdb, 0xaf, 0x0e, 0x12, 0xeb, 0x66, 0xd4, 0xc3, 0x8e, 0xd4, 0x69,
0xc7, 0x55, 0x6d, 0x1a, 0xd9, 0x3b, 0x23, 0x5c, 0x86, 0xdb, 0xda, 0x8f,
0x36, 0xac, 0xed, 0x64, 0x07, 0x97, 0x83, 0x2e, 0xe6, 0xd1, 0xde, 0xb0,
0x7d, 0x0d, 0x2e, 0xb0, 0x4f, 0x39, 0x8c, 0x8c, 0x07, 0xe1, 0x89, 0x0d,
0x90, 0x62, 0xd6, 0x92, 0x3e, 0xea, 0x4c, 0xd9, 0x3d, 0x71, 0x54, 0x15,
0xb8, 0x4e, 0xab, 0x36, 0x05, 0x8f, 0x5e, 0xc6, 0x6a, 0x6c, 0x72, 0xae,
0x5b, 0x23, 0xec, 0xbd, 0x67, 0x4f, 0xde, 0x32, 0x62, 0x7d, 0xa1, 0x65,
0x70, 0xe3, 0x42, 0x16, 0x17, 0x49, 0xda, 0xb1, 0xb1, 0xca, 0xd2, 0x9a,
0x9c, 0xd2, 0x97, 0x1f, 0x61, 0xe1, 0xe2, 0x45, 0x90, 0x50, 0x93, 0xa2,
0x45, 0xde, 0x59, 0xed, 0x42, 0x3a, 0x92, 0x8c, 0x8f, 0xa3, 0x2c, 0x4c,
0xb1, 0xd7, 0x61, 0x2c, 0x78, 0x19, 0xe1, 0xaf, 0x96, 0x1c, 0xfb, 0x33,
0x7a, 0xc4, 0xdd, 0x74, 0xec, 0xd1, 0x5a, 0x0c, 0x29, 0xac, 0x93, 0x48,
0x2e, 0x84, 0x0a, 0xfc, 0xd7, 0x4c, 0x48, 0x49, 0x5e, 0x9c, 0xb3, 0xb8,
0x52, 0xbd, 0xdc, 0x31, 0xba, 0x93, 0xd0, 0x47, 0x76, 0x65, 0x7e, 0x6b,
0x3c, 0x59, 0xdc, 0x53, 0x84, 0x41, 0xc2, 0x64, 0xbb, 0xa4, 0x20, 0xa6,
0x86, 0x34, 0x16, 0x93, 0xe8, 0xb8, 0xcd, 0x69, 0x5d, 0xca, 0xa6, 0x60,
0x4a, 0xca, 0xc7, 0xcd, 0x87, 0xee, 0x7b, 0x33, 0xbc, 0x5a, 0x80, 0xe4,
0xd0, 0xa0, 0xb7, 0xe8, 0x4a, 0xeb, 0xab, 0x53, 0xcb, 0x4d, 0xa4, 0xa0,
0x66, 0xb6, 0x8c, 0xc8, 0x1d, 0xf4, 0xb1, 0x3b, 0xcd, 0x4c, 0x14, 0xaf,
0x85, 0x41, 0x8c, 0x71, 0xf2, 0x98, 0xbc, 0x8b, 0xea, 0x59, 0x0b, 0x36,
0xc9, 0x07, 0x57, 0xe3, 0x48, 0x79, 0x72, 0xd2, 0xc5, 0x68, 0x18, 0xd5,
0x48, 0xe0, 0x2b, 0x30, 0xed, 0x1f, 0x58, 0x73, 0x98, 0x77, 0x9b, 0x0e,
0xd7, 0x8f, 0x88, 0xe6, 0xa3, 0x19, 0x1d, 0xb4, 0x0f, 0x7a, 0xbc, 0xb7,
0x4d, 0x1e, 0xc1, 0xb5, 0x3b, 0x9b, 0x43, 0xf3, 0x9d, 0x14, 0x97, 0xaf,
0x31, 0x78, 0x70, 0x0b, 0x75, 0x8f, 0x17, 0x47, 0x0f, 0x2f, 0x77, 0x9e,
0x21, 0x50, 0x1e, 0x74, 0x9b, 0x08, 0x1d, 0xa8, 0x5e, 0x8b, 0xbc, 0x87,
0x33, 0xc9, 0x3d, 0xac, 0x75, 0xf4, 0xe1, 0xbd, 0xf3, 0x03, 0xb4, 0x51,
0x58, 0xed, 0x34, 0x72, 0xe5, 0x6c, 0xf9, 0x5c, 0xf5, 0xe5, 0x03, 0xe8,
0xe6, 0xa1, 0x0c, 0xa5, 0xfa, 0x0c, 0xb9, 0x51, 0x4d, 0xc3, 0x32, 0xab,
0x12, 0xb5, 0xe1, 0xeb, 0x5d, 0x1b, 0xde, 0x51, 0xbd, 0x73, 0xf1, 0x0f,
0xbd, 0x41, 0x86, 0x04, 0xa5, 0xa2, 0xb0, 0x7d, 0xe7, 0x38, 0xcb, 0xdc,
0x4b, 0x13, 0xa3, 0xb6, 0xe4, 0x29, 0xcf, 0x21, 0x17, 0x65, 0x87, 0xbc,
0x91, 0xad, 0x95, 0x94, 0x37, 0xb5, 0xc0, 0x6b, 0xd9, 0xc4, 0xe4, 0x44,
0x3e, 0x75, 0x7a, 0x16, 0xc9, 0x97, 0x1b, 0x67, 0x4d, 0x1b, 0x0d, 0x6c,
0x70, 0xe8, 0xc0, 0x48, 0x1d, 0xba, 0x91, 0x34, 0xa2, 0x90, 0x3c, 0x9b,
0xea, 0x43, 0x9b, 0x05, 0x51, 0xd7, 0x39, 0x64, 0x13, 0xdd, 0xf6, 0xa0,
0x35, 0xea, 0x87, 0x84, 0xda, 0xc8, 0xf2, 0x9d, 0x60, 0xa9, 0xfd, 0x93,
0xaa, 0x86, 0x8c, 0x88, 0x84, 0x18, 0xd7, 0xc3, 0xc5, 0xd6, 0x9c, 0x48,
0x92, 0x46, 0xfc, 0xf1, 0x7d, 0xf0, 0x69, 0x00, 0xe8, 0xdd, 0x05, 0xa2,
0xbb, 0x6e, 0xec, 0xa7, 0x11, 0x53, 0x38, 0x43, 0x9e, 0x1e, 0xf5, 0x5a,
0x77, 0x30, 0xd5, 0x9a, 0xfd, 0x5c, 0x1a, 0x83, 0x40, 0xc4, 0xb3, 0x5c,
0x13, 0x1e, 0x34, 0xb8, 0xdc, 0xa4, 0xda, 0x34, 0xc9, 0xbf, 0xb4, 0x05,
0xe0, 0x04, 0x68, 0xee, 0xfc, 0x8d, 0x32, 0xfb, 0x95, 0x07, 0x19, 0x81,
0x3e, 0x55, 0x3c, 0x06, 0xbc, 0x7b, 0x7a, 0x65, 0x42, 0xad, 0xaa, 0x54,
0xff, 0x0b, 0xb9, 0x22, 0x9c, 0x28, 0x48, 0x0c, 0xc9, 0x56, 0x6c, 0x00
};
static const uint8_t fx_b[1375] = {
0x42, 0x5a, 0x68, 0x31, 0x31, 0x41, 0x59, 0x26, 0x53, 0x59, 0x53, 0x7c,
0x70, 0xe5, 0x00, 0x28, 0x08, 0x17, 0x80, 0x40, 0x00, 0x3f, 0xff, 0xff,
0xf0, 0x3f, 0xff, 0xff, 0xf0, 0x40, 0x02, 0xf8, 0x76, 0xee, 0x03, 0xa0,
0x23, 0x0f, 0x50, 0xd3, 0x10, 0x34, 0x00, 0x00, 0x00, 0x00, 0x68, 0x21,
0x4d, 0x31, 0x27, 0xa6, 0x53, 0x00, 0x4d, 0x18, 0x00, 0x02, 0x69, 0x89,
0x81, 0x35, 0x55, 0x3f, 0x4a, 0x86, 0xa9, 0xfa, 0x26, 0x29, 0xe9, 0x1e,
0xa3, 0xf5, 0x46, 0x69, 0x94, 0xf5, 0x1a, 0x7a, 0x62, 0x9b, 0x26, 0x9e,
0xa4, 0x11, 0x51, 0x10, 0x68, 0x00, 0x1a, 0x00, 0x06, 0x80, 0x00, 0x38,
0x77, 0x6c, 0xfa, 0xf8, 0x66, 0xb3, 0xcb, 0xd2, 0xd6, 0x70, 0xd6, 0xce,
0x4e, 0x2f, 0x0b, 0x90, 0x25, 0x1c, 0xdd, 0x79, 0x44, 0x9b, 0xa8, 0x94,
0x74, 0xf4, 0x62, 0x24, 0xe6, 0x44, 0xa3, 0xdf, 0xf1, 0xef, 0xd5, 0x12,
0x73, 0xa2, 0x51, 0xd7, 0xb7, 0x11, 0x27, 0x3a, 0x25, 0x1e, 0xef, 0xeb,
0x11, 0x27, 0xe7, 0x44, 0xa3, 0xf6, 0xd6, 0x22, 0x4d, 0x04, 0xa3, 0xb3,
0xf2, 0xca, 0x24, 0xea, 0xbd, 0xed, 0x96, 0xcb, 0x65, 0xb0, 0x52, 0x8d,
0x77, 0xf7, 0x69, 0x44, 0x46, 0x51, 0x41, 0x05, 0xb9, 0x34, 0xf2, 0x08,
0x88, 0xca, 0x40, 0xef, 0x3d, 0x11, 0x9f, 0xdc, 0xf2, 0xd4, 0xe3, 0xd2,
0xa9, 0xe3, 0xf1, 0x47, 0xee, 0xfd, 0x80, 0x2d, 0xe8, 0xa3, 0x1b, 0x60,
0x71, 0x10, 0xc7, 0x28, 0x7c, 0x59, 0x52, 0x30, 0x56, 0x78, 0x0d, 0x5a,
0xac, 0xa3, 0x0c, 0x20, 0x99, 0x88, 0x76, 0xa0, 0x12, 0x0c, 0x0a, 0x36,
0xe3, 0x32, 0x9c, 0x2b, 0x5e, 0x79, 0x5c, 0xe0, 0x77, 0x94, 0x52, 0x9f,
0xa2, 0x30, 0x92, 0x78, 0x6d, 0x30, 0xa0, 0xc8, 0x59, 0x09, 0x2a, 0xe8,
0x88, 0x40, 0x44, 0x04, 0x00, 0x46, 0x5b, 0x08, 0x22, 0x4e, 0x25, 0x45,
0x1d, 0x9e, 0x19, 0x44, 0x9f, 0xa8, 0x4a, 0x3d, 0x78, 0xb2, 0x12, 0x6a,
0x12, 0x8c, 0xff, 0x71, 0x12, 0x74, 0x84, 0xa3, 0x8f, 0xe9, 0x94, 0x22,
0xaf, 0x3e, 0x3c, 0x24, 0x9b, 0xf6, 0x36, 0x49, 0x28, 0xf1, 0xfb, 0xf8,
0xe8, 0x24, 0xdc, 0x89, 0x47, 0xdb, 0xcb, 0x02, 0x4f, 0xd0, 0x92, 0x8f,
0x3e, 0x5e, 0x6d, 0x04, 0x9f, 0x15, 0x45, 0x19, 0xf8, 0xc5, 0x11, 0x57,
0xfd, 0xc3, 0xa3, 0x50, 0x93, 0xbd, 0x51, 0x47, 0x27, 0xb6, 0x51, 0x27,
0x32, 0x25, 0x1d, 0x3a, 0xc0, 0x93, 0xe6, 0x12, 0x8e, 0x8e, 0x19, 0x09,
0x34, 0x12, 0x8e, 0x2f, 0x4c, 0x44, 0x9b, 0x51, 0x28, 0xf7, 0x7a, 0xe0,
0x49, 0xdb, 0x44, 0xa3, 0xdb, 0x7f, 0x3e, 0xa1, 0x25, 0x1d, 0xb8, 0x46,
0x31, 0x8d, 0x62, 0x12, 0x46, 0x02, 0x80, 0x06, 0xc8, 0x02, 0x08, 0x25,
0x99, 0xa2, 0x8a, 0x23, 0x2a, 0x0c, 0xa3, 0xbb, 0x76, 0x04, 0x9b, 0x54,
0x94, 0x71, 0xf5, 0x76, 0xea, 0x12, 0x72, 0x92, 0x51, 0xd9, 0xed, 0x81,
0x27, 0x92, 0x25, 0x1f, 0x7f, 0x96, 0x04, 0x9c, 0xaa, 0x8a, 0x37, 0xf2,
0x65, 0x12, 0x7d, 0x09, 0x28, 0xe8, 0xd7, 0xf1, 0xa0, 0x93, 0xd2, 0xa8,
0xa3, 0x87, 0x1e, 0x51, 0x27, 0x9a, 0x25, 0x1d, 0x7d, 0xb8, 0x12, 0x6e,
0x48, 0xa3, 0xab, 0x6e, 0x52, 0x45, 0x5b, 0xbf, 0x1d, 0xda, 0x84, 0x9f,
0x6f, 0xad, 0xb2, 0xa8, 0xa3, 0xf9, 0xe5, 0xca, 0x24, 0xd1, 0x25, 0x1d,
0x7f, 0x2c, 0x09, 0x3f, 0xc2, 0x4a, 0x3c, 0x7b, 0x76, 0x61, 0x11, 0x56,
0xdf, 0x9e, 0xdd, 0x11, 0x15, 0x67, 0x86, 0x04, 0x9c, 0x11, 0x28, 0xdf,
0xcb, 0x81, 0x27, 0xaa, 0x25, 0x1e, 0x3b, 0xf0, 0x24, 0x7f, 0x98, 0xa0,
0xac, 0x93, 0x29, 0xac, 0x9b, 0x67, 0x14, 0xe3, 0x00, 0x5a, 0xa4, 0x8b,
0xc0, 0x20, 0x00, 0x1f, 0xff, 0xff, 0xf8, 0x1f, 0xff, 0xff, 0xf8, 0x20,
0x01, 0x7c, 0x1b, 0x60, 0x00, 0x04, 0x28, 0x34, 0x03, 0x40, 0x1a, 0x00,
0x00, 0x00, 0x00, 0x54, 0xd3, 0x6a, 0x01, 0xa6, 0x80, 0x00, 0x03, 0x40,
0x00, 0x00, 0x9a, 0xaa, 0x65, 0x11, 0x36, 0xa3, 0xd4, 0xca, 0x7a, 0x4f,
0xd4, 0xc4, 0xf4, 0x50, 0x6c, 0x50, 0xf4, 0xd3, 0x6a, 0x69, 0xb5, 0x21,
0x80, 0x1a, 0x68, 0xd0, 0x69, 0x88, 0x00, 0x00, 0x06, 0x40, 0x7a, 0x71,
0xef, 0xfb, 0x70, 0xc7, 0x95, 0xe3, 0x69, 0xc7, 0x1a, 0xb5, 0xe3, 0x3c,
0x9c, 0xbc, 0x95, 0x02, 0x7e, 0xfb, 0x76, 0xe4, 0x11, 0xae, 0xa0, 0x4e,
0xbd, 0xdf, 0x2c, 0xd5, 0x43, 0xdf, 0x20, 0x4d, 0xdd, 0x18, 0x55, 0x0f,
0x84, 0x81, 0x37, 0xee, 0xc2, 0xa8, 0x73, 0xc8, 0x13, 0x5f, 0x26, 0x2a,
0xa1, 0xce, 0xa0, 0x4e, 0xde, 0xef, 0xef, 0x20, 0x8d, 0x52, 0x04, 0xed,
0xea, 0xc5, 0x54, 0x3e, 0x77, 0xcd, 0xa6, 0x9a, 0x5a, 0x5a, 0x69, 0x48,
0x27, 0xd3, 0x7e, 0x20, 0x8c, 0xd2, 0x09, 0xdb, 0xcd, 0x88, 0x23, 0x36,
0x97, 0x37, 0x1f, 0x2f, 0x5d, 0x9c, 0x7c, 0xf5, 0x6f, 0xd5, 0xc3, 0xce,
0xcf, 0x1d, 0xc6, 0x7e, 0xf9, 0x31, 0x46, 0x42, 0x71, 0xf2, 0x7e, 0x3c,
0xc9, 0x81, 0xd6, 0xfb, 0x22, 0x6a, 0xa2, 0x26, 0x01, 0xc6, 0x8e, 0x07,
0x31, 0x6c, 0x02, 0x91, 0x71, 0x31, 0xf0, 0x9b, 0x58, 0x1f, 0x8d, 0x64,
0x0e, 0xf9, 0x38, 0xcc, 0x40, 0x80, 0x86, 0xf9, 0x17, 0x4e, 0x79, 0xb3,
0xc6, 0x32, 0x42, 0x0a, 0x14, 0xa9, 0x48, 0x65, 0xcc, 0xc3, 0xee, 0xa6,
0xd8, 0x80, 0x08, 0x88, 0x88, 0x0f, 0x78, 0x9e, 0x0a, 0x86, 0x48, 0x13,
0x8f, 0xa7, 0x7e, 0x41, 0x1b, 0x94, 0x09, 0x9e, 0x18, 0x95, 0x0c, 0x90,
0x27, 0xeb, 0x3c, 0xd9, 0xaa, 0x87, 0x75, 0x01, 0x3f, 0xee, 0xfc, 0x48,
0x8a, 0xb8, 0x5a, 0x55, 0x43, 0xc2, 0xd2, 0xd2, 0xa0, 0x4e, 0x5f, 0x5c,
0x4a, 0x87, 0x2c, 0x81, 0x33, 0xed, 0x82, 0xa1, 0xd9, 0x20, 0x4d, 0xbc,
0x71, 0x2a, 0x1f, 0x48, 0x04, 0xfc, 0xfc, 0x71, 0x51, 0x15, 0x63, 0xcf,
0x12, 0xa1, 0xd7, 0x40, 0x4f, 0xc7, 0xed, 0x8a, 0xa8, 0x6c, 0x90, 0x27,
0x4f, 0xdf, 0x05, 0x43, 0xca, 0xa0, 0x4e, 0x1d, 0x3e, 0xb9, 0x95, 0x0d,
0x54, 0x04, 0xeb, 0xd9, 0x8a, 0xa8, 0x73, 0x28, 0x13, 0x97, 0xb3, 0x05,
0x43, 0xd2, 0x40, 0x9c, 0xfd, 0xb8, 0x2a, 0x1c, 0x9f, 0x5f, 0x0c, 0x7d,
0xb3, 0x9c, 0x10, 0x27, 0xe7, 0xf8, 0xc5, 0x54, 0x35, 0x88, 0x27, 0xcb,
0x8f, 0x4e, 0x65, 0x43, 0x96, 0xa0, 0x4f, 0x7f, 0xb6, 0x25, 0x43, 0x65,
0x40, 0x98, 0xfd, 0x60, 0xa8, 0x78, 0xd4, 0x09, 0xd5, 0xdd, 0xbf, 0x25,
0x43, 0x9a, 0x80, 0x98, 0xe3, 0x8a, 0xa8, 0x7a, 0x48, 0x13, 0xe1, 0xd5,
0x8a, 0xa8, 0x7b, 0x69, 0x68, 0x40, 0x9a, 0xfa, 0x34, 0xc0, 0x23, 0xef,
0x50, 0x27, 0xf9, 0xbb, 0x6e, 0x4a, 0x86, 0xc9, 0x55, 0x47, 0x47, 0x66,
0x29, 0x22, 0xaf, 0xae, 0xbc, 0x4a, 0x87, 0xaf, 0x0d, 0x34, 0xb4, 0x90,
0x27, 0x8e, 0xec, 0x55, 0x43, 0x22, 0x09, 0x8f, 0x8e, 0x0a, 0x87, 0x8c,
0x81, 0x37, 0x7b, 0xb1, 0x42, 0x2a, 0xd9, 0xb7, 0x0a, 0xaa, 0xd9, 0xa1,
0x12, 0xab, 0xa7, 0x7e, 0x0a, 0x87, 0xb0, 0x82, 0x7f, 0xbe, 0x1a, 0xb2,
0x54, 0x3f, 0x15, 0x02, 0x7b, 0xb6, 0xe1, 0x4a, 0x78, 0x68, 0xa2, 0xaf,
0xfc, 0xc5, 0x05, 0x64, 0x99, 0x4d, 0x65, 0x0e, 0x8e, 0x65, 0x84, 0x00,
0xf3, 0x20, 0x5e, 0x01, 0x00, 0x00, 0xfb, 0x7e, 0xfe, 0xc0, 0xff, 0xff,
0xff, 0xc1, 0x00, 0x09, 0xe1, 0x40, 0x01, 0x88, 0x50, 0xd1, 0xa3, 0x41,
0xa0, 0x01, 0xa0, 0x00, 0x34, 0x68, 0x0c, 0x01, 0xa6, 0x86, 0x8d, 0x18,
0x8c, 0x80, 0x68, 0x00, 0xd0, 0xc1, 0x35, 0x4a, 0x50, 0xc8, 0x8c, 0x4d,
0x1a, 0x69, 0x89, 0xa6, 0x99, 0x34, 0xc4, 0x66, 0xa7, 0xa6, 0x90, 0xc0,
0x1a, 0x68, 0x68, 0xd1, 0x88, 0xc8, 0x06, 0x80, 0x0d, 0x0c, 0xec, 0xf6,
0xcf, 0x2d, 0xde, 0x3b, 0x3c, 0x38, 0x36, 0x6b, 0xe0, 0x20, 0x9d, 0x78,
0x50, 0xd7, 0x40, 0x9c, 0x99, 0x28, 0x71, 0x10, 0x4e, 0xbc, 0x28, 0x6a,
0x90, 0x4e, 0x9c, 0x28, 0x71, 0x10, 0x4d, 0x59, 0x28, 0x7c, 0x48, 0x27,
0x46, 0x14, 0x34, 0xa0, 0x4e, 0x5c, 0x94, 0x38, 0xf9, 0x2c, 0xb3, 0x14,
0x13, 0x56, 0x2a, 0x1a, 0x28, 0x27, 0x36, 0x2a, 0x1a, 0x59, 0xe3, 0xcd,
0xc7, 0x9a, 0xfd, 0x3c, 0x3d, 0xf5, 0x11, 0x2e, 0x14, 0x3c, 0x36, 0xe6,
0x2c, 0xaa, 0x4a, 0x54, 0x63, 0x76, 0xaf, 0x1d, 0x71, 0xa0, 0x74, 0x8a,
0xf8, 0x88, 0xf5, 0x70, 0x44, 0x9f, 0x60, 0x77, 0xce, 0x53, 0x88, 0x80,
0x88, 0x88, 0x3b, 0x7f, 0x5a, 0x14, 0x37, 0xa8, 0x13, 0x76, 0x14, 0x3a,
0x24, 0x13, 0xdf, 0xd7, 0x4a, 0x43, 0x79, 0x02, 0x7a, 0xe4, 0xa1, 0xda,
0x81, 0x36, 0x64, 0x22, 0xad, 0xb9, 0x28, 0x73, 0x65, 0x84, 0x13, 0x87,
0x29, 0x0e, 0x02, 0x09, 0xc7, 0x94, 0x87, 0x2d, 0x02, 0x6c, 0xc2, 0x87,
0x54, 0x04, 0xf6, 0xc4, 0x45, 0x5e, 0x99, 0x48, 0x73, 0x90, 0x4f, 0x4e,
0x5d, 0x25, 0x0e, 0x19, 0x04, 0xe0, 0xca, 0x43, 0xa8, 0x82, 0x6f, 0xe5,
0x21, 0xa5, 0x02, 0x6d, 0xc1, 0x36, 0x61, 0x0b, 0x54, 0x82, 0x73, 0xfc,
0xb4, 0xa4, 0x3f, 0x04, 0x13, 0x97, 0x29, 0x0f, 0xdf, 0x67, 0x87, 0x0e,
0xf5, 0x02, 0x6c, 0xc9, 0x43, 0x45, 0x2a, 0x7d, 0xf1, 0x50, 0x7d, 0x32,
0x90, 0xd5, 0x20, 0x9e, 0xb9, 0x48, 0x6b, 0x90, 0x4f, 0xf6, 0x52, 0x1f,
0xd2, 0x09, 0xb7, 0x29, 0x0d, 0x48, 0x13, 0xdb, 0x25, 0x0f, 0x3a, 0x04,
0xf7, 0xca, 0x43, 0x6d, 0x02, 0x75, 0xe1, 0x43, 0xd4, 0x82, 0x6e, 0xca,
0x43, 0x55, 0x55, 0x46, 0x98, 0x48, 0xab, 0x8b, 0x29, 0x0f, 0x3d, 0x94,
0x09, 0xbf, 0x92, 0x86, 0x95, 0x04, 0xe9, 0xef, 0xd2, 0x90, 0xef, 0xa0,
0x4f, 0x86, 0x04, 0x55, 0xef, 0x81, 0x15, 0x77, 0x65, 0x21, 0xba, 0x81,
0x3c, 0xb0, 0xa1, 0xb6, 0x81, 0x3b, 0xb0, 0xa0, 0xc1, 0x77, 0x24, 0x53,
0x85, 0x09, 0x06, 0x3c, 0xe0, 0x97, 0x80
};
static const uint8_t fx_c[1254] = {
0x42, 0x5a, 0x68, 0x32, 0x31, 0x41, 0x59, 0x26, 0x53, 0x59, 0x6a, 0xab,
0xe5, 0x9f, 0x00, 0x02, 0x76, 0x7f, 0xff, 0xf3, 0xbb, 0xeb, 0xbf, 0xfe,
0x3f, 0xff, 0xff, 0xed, 0xfb, 0xff, 0xef, 0xff, 0xfe, 0xf5, 0xfd, 0x73,
0xd7, 0xee, 0xef, 0xff, 0xdd, 0x37, 0xab, 0xff, 0xfe, 0x3f, 0x7d, 0xfd,
0xb9, 0x5f, 0x58, 0xd0, 0x03, 0x5e, 0x34, 0x00, 0x05, 0x1b, 0x62, 0x29,
0x40, 0x0d, 0x1a, 0x00, 0xd0, 0x1a, 0x0c, 0x83, 0x40, 0x00, 0x1a, 0x68,
0x00, 0x34, 0x00, 0xc8, 0x32, 0x00, 0x00, 0x00, 0xc9, 0x91, 0x90, 0x00,
0x00, 0x00, 0x69, 0x90, 0x34, 0x68, 0xd0, 0x34, 0x1a, 0x01, 0xa0, 0x0d,
0x00, 0x01, 0x12, 0x44, 0xd0, 0x1a, 0x00, 0x68, 0x00, 0x68, 0x00, 0x0d,
0x00, 0x00, 0x1a, 0x00, 0x64, 0x01, 0xa0, 0x00, 0xd0, 0xd3, 0x40, 0xd1,
0xa0, 0x68, 0xd1, 0xa0, 0x68, 0x03, 0x40, 0x68, 0x00, 0x00, 0x00, 0x00,
0x00, 0x1a, 0x00, 0x10, 0x68, 0x1a, 0x01, 0x90, 0x64, 0x1a, 0x06, 0x81,
0xa3, 0x13, 0x23, 0x40, 0xd0, 0x01, 0x88, 0x00, 0xd0, 0x30, 0x86, 0x8d,
0x03, 0x40, 0x00, 0x00, 0x0d, 0x01, 0x91, 0xa3, 0x40, 0x00, 0x00, 0x00,
0x34, 0x64, 0x64, 0xc9, 0x89, 0xa0, 0x19, 0x34, 0x62, 0x0d, 0x03, 0x40,
0x32, 0x0c, 0x83, 0x40, 0xd0, 0x34, 0x62, 0x64, 0x68, 0x1a, 0x00, 0x31,
0x00, 0x1a, 0x06, 0x10, 0xd1, 0xa0, 0x68, 0x00, 0x00, 0x01, 0xa0, 0x32,
0x34, 0x68, 0x00, 0x00, 0x00, 0x06, 0x8c, 0x8c, 0x99, 0x31, 0x34, 0x03,
0x26, 0x8c, 0x12, 0x50, 0x91, 0xa2, 0x23, 0x34, 0x48, 0xf4, 0xd4, 0x3d,
0x43, 0xd2, 0x6d, 0x35, 0x32, 0x7a, 0x69, 0x1a, 0x31, 0x31, 0xa2, 0x6c,
0x4d, 0x4f, 0x49, 0xe8, 0xc9, 0x3d, 0x23, 0x47, 0xa1, 0x93, 0x49, 0xea,
0x66, 0xa3, 0xd4, 0xf2, 0x13, 0x1a, 0x9a, 0x66, 0xa6, 0xf5, 0x06, 0x4d,
0x4f, 0x48, 0x1b, 0x23, 0x46, 0x8d, 0x4d, 0x8a, 0x69, 0xb5, 0x18, 0xc8,
0x68, 0x9a, 0x66, 0xa3, 0x34, 0xc4, 0x4d, 0x1e, 0x4c, 0x98, 0xa7, 0x89,
0x86, 0x16, 0xed, 0xb9, 0x19, 0x36, 0x9d, 0x3b, 0x08, 0x68, 0x30, 0x18,
0xa7, 0xf2, 0xc5, 0x53, 0x21, 0x63, 0x47, 0x89, 0xa6, 0x77, 0xb1, 0x22,
0x05, 0x87, 0x3b, 0x8a, 0xe3, 0xc0, 0xc3, 0xa1, 0x09, 0x66, 0xaa, 0xf8,
0x12, 0x82, 0x0c, 0x0a, 0xab, 0xa9, 0xe8, 0x53, 0x4c, 0x9d, 0xce, 0x01,
0x65, 0x9a, 0x68, 0x6e, 0xa0, 0x29, 0x9d, 0xf4, 0xc2, 0x4a, 0x26, 0xee,
0x06, 0x80, 0x53, 0x8d, 0xaf, 0xbb, 0x92, 0x4b, 0x21, 0x87, 0xd1, 0xf6,
0x41, 0x8b, 0x1c, 0x26, 0x98, 0x3a, 0x51, 0x1c, 0x5d, 0x8e, 0xa0, 0x9c,
0x83, 0x15, 0xd7, 0x9d, 0x97, 0x8e, 0xfc, 0xea, 0x0c, 0x09, 0xe2, 0xfb,
0x48, 0xde, 0x33, 0xed, 0xd6, 0x0b, 0x7b, 0xcc, 0x6a, 0x76, 0xb4, 0xae,
0x64, 0x80, 0x93, 0x0a, 0x0e, 0x09, 0x81, 0xb6, 0x4b, 0x08, 0x81, 0x22,
0xe0, 0x25, 0x6c, 0xc9, 0xd6, 0xb4, 0xb8, 0x89, 0x20, 0x0b, 0xa1, 0x90,
0x8c, 0x95, 0x8e, 0x60, 0x04, 0x95, 0x1b, 0xcd, 0x95, 0xaa, 0x74, 0xa0,
0x88, 0xfb, 0x1f, 0x22, 0xc1, 0x35, 0x3e, 0x21, 0x6b, 0xd8, 0x66, 0x68,
0xf8, 0x0f, 0xd2, 0x33, 0x71, 0x23, 0x52, 0x92, 0x75, 0x2f, 0xcc, 0x2f,
0x77, 0xcd, 0xf9, 0xc5, 0x4d, 0x79, 0x6f, 0xfa, 0xe7, 0x94, 0x62, 0x8b,
0x8a, 0xe0, 0x2e, 0x18, 0x14, 0x6b, 0xc1, 0xc7, 0x7f, 0x16, 0xb5, 0x12,
0xf1, 0xed, 0x98, 0x50, 0x22, 0xb5, 0x79, 0xdf, 0xbd, 0xb6, 0x85, 0x79,
0x2f, 0x6b, 0x85, 0xd6, 0xe1, 0x2a, 0xfe, 0x19, 0x4c, 0xca, 0xa8, 0xfd,
0x77, 0x10, 0x54, 0xbe, 0x5a, 0xf5, 0x41, 0x60, 0xa1, 0x69, 0xa1, 0xea,
0xe4, 0xc4, 0xdc, 0x3b, 0x14, 0x35, 0x9a, 0x8f, 0xb5, 0x52, 0x3d, 0x81,
0xbf, 0x0e, 0xcb, 0x33, 0xec, 0x79, 0x64, 0xfe, 0x08, 0x21, 0xfa, 0x6c,
0x1e, 0xbf, 0x6a, 0xd3, 0xaa, 0xe7, 0xa8, 0xff, 0x21, 0x7a, 0x18, 0x79,
0x7a, 0xc8, 0x3b, 0xcb, 0x40, 0x31, 0x18, 0x11, 0xf4, 0xdc, 0xb3, 0xd1,
0x66, 0x89, 0x4c, 0xe7, 0x1a, 0xb6, 0xaa, 0xce, 0x89, 0x4a, 0x47, 0x75,
0xf6, 0x0f, 0x20, 0x74, 0x67, 0x31, 0x17, 0x6c, 0xfb, 0xad, 0x8d, 0xfb,
0x37, 0x89, 0xf3, 0x3a, 0xc8, 0x9e, 0x07, 0x92, 0x37, 0xcc, 0x07, 0xbf,
0x1e, 0x78, 0xad, 0x2d, 0xd4, 0x96, 0x25, 0x5a, 0xd3, 0x73, 0xae, 0x14,
0xad, 0xca, 0xe8, 0xd8, 0xa8, 0xea, 0x46, 0xab, 0xd8, 0xfb, 0xe9, 0x35,
0x59, 0xd7, 0xb1, 0x50, 0xcb, 0x26, 0x8a, 0x35, 0x73, 0xfa, 0xd8, 0xbc,
0x3a, 0x01, 0x42, 0x3d, 0x86, 0x46, 0xb8, 0x59, 0x86, 0x27, 0x49, 0xf4,
0xc2, 0x75, 0x87, 0x27, 0x47, 0x6f, 0x51, 0xda, 0x72, 0xea, 0xc8, 0xb5,
0x6d, 0x29, 0x37, 0x22, 0x6e, 0x5c, 0xb5, 0x70, 0x55, 0x30, 0xf1, 0xd9,
0x28, 0x07, 0x47, 0x01, 0xc2, 0xf7, 0x98, 0xb3, 0x36, 0xf5, 0xcd, 0x1f,
0x2b, 0x78, 0x2e, 0x41, 0x66, 0x9e, 0x24, 0x9a, 0x28, 0xa2, 0xd7, 0xbc,
0x7f, 0x6e, 0x13, 0xf7, 0xfc, 0xa5, 0x4f, 0x84, 0xff, 0x6b, 0xa0, 0x2e,
0x5a, 0x71, 0x22, 0xd0, 0xb0, 0x61, 0x4f, 0x59, 0x40, 0xa0, 0xf8, 0x5a,
0x71, 0x19, 0xd8, 0xbd, 0xed, 0x8b, 0x74, 0x81, 0xf2, 0xe5, 0x87, 0x53,
0x11, 0xe0, 0x31, 0x98, 0x14, 0x3a, 0x64, 0xfd, 0xe7, 0xc7, 0xec, 0xdf,
0xa9, 0xe5, 0x0a, 0x71, 0x48, 0x47, 0x76, 0xf4, 0x4f, 0x20, 0xe6, 0xf5,
0x14, 0x02, 0xa3, 0x3f, 0xbb, 0x78, 0x14, 0xdd, 0xbd, 0x0e, 0x0d, 0xcb,
0x48, 0x73, 0x4d, 0x59, 0x82, 0xc5, 0x81, 0x02, 0x01, 0x6d, 0xdc, 0x79,
0x2a, 0x86, 0x18, 0xd1, 0x02, 0xd1, 0x80, 0x43, 0x84, 0x5c, 0x32, 0xe7,
0xb9, 0x00, 0x5a, 0x56, 0x2b, 0x35, 0x37, 0xc2, 0xec, 0xfc, 0x37, 0x0b,
0x66, 0xaf, 0xf0, 0xf3, 0xaa, 0x3b, 0xdf, 0x6a, 0xe9, 0x2c, 0x9e, 0x46,
0xf3, 0x1f, 0x61, 0x74, 0xc4, 0x64, 0x6e, 0x63, 0x74, 0x6f, 0x0b, 0x64,
0x9c, 0x6d, 0x3b, 0xae, 0x4b, 0x55, 0xfe, 0x34, 0x7e, 0x57, 0xc4, 0xba,
0x8a, 0x1d, 0x43, 0x4d, 0x0b, 0x3e, 0xaa, 0xf3, 0x1d, 0x81, 0x04, 0xcd,
0xcd, 0xb8, 0x56, 0xcb, 0x5b, 0x07, 0xc6, 0xa2, 0x2c, 0x1f, 0x32, 0xb7,
0x4f, 0xa9, 0xbb, 0x51, 0xdf, 0xf3, 0x57, 0x30, 0x52, 0x0f, 0x60, 0xb8,
0x98, 0x65, 0xd6, 0xdc, 0x50, 0x02, 0x0b, 0x50, 0x48, 0x13, 0x4c, 0x07,
0x8c, 0x1f, 0x0b, 0x85, 0x38, 0xb4, 0x15, 0x76, 0xac, 0x1f, 0x31, 0x9a,
0x2e, 0xaa, 0x71, 0x64, 0xd1, 0x5a, 0x4b, 0x30, 0x93, 0xc2, 0x6a, 0x8c,
0xd8, 0xb0, 0x96, 0xdb, 0x50, 0xc6, 0xd3, 0xe2, 0x0b, 0xcf, 0xf0, 0xea,
0xea, 0xb5, 0xbb, 0x79, 0x25, 0xa9, 0x8c, 0xe4, 0x62, 0x8b, 0xfa, 0x4e,
0x96, 0x6a, 0xaa, 0x4e, 0x35, 0x3a, 0x47, 0x20, 0xbf, 0x4f, 0x50, 0x35,
0x77, 0xa6, 0x5e, 0xe5, 0xf2, 0x44, 0xca, 0xdd, 0xbb, 0x0c, 0x78, 0x95,
0xe2, 0x7e, 0xfa, 0x79, 0xc9, 0xd3, 0x78, 0xe2, 0x9f, 0xca, 0x95, 0xcb,
0xac, 0x5e, 0x6b, 0x48, 0x9b, 0x3e, 0xf2, 0x8d, 0x3d, 0x46, 0x8a, 0x62,
0xe9, 0x58, 0x4c, 0xd8, 0x2c, 0xdf, 0x7f, 0x80, 0x4c, 0x99, 0x78, 0xec,
0xf1, 0xe1, 0x43, 0x18, 0x70, 0x60, 0xf8, 0x21, 0x5c, 0x68, 0xf8, 0xc0,
0x49, 0x10, 0xe0, 0xbd, 0x1b, 0x14, 0x9a, 0x43, 0xc2, 0x48, 0x24, 0xd0,
0xc2, 0x1c, 0x02, 0x50, 0x73, 0x03, 0x43, 0x00, 0x10, 0xb4, 0xab, 0x1e,
0x28, 0x74, 0xac, 0x20, 0x4b, 0xb5, 0x6e, 0x44, 0xed, 0x93, 0x22, 0x38,
0x55, 0x2a, 0x52, 0x3f, 0x27, 0x9c, 0xd1, 0xfb, 0xc6, 0xab, 0x5b, 0xd6,
0x1e, 0x4a, 0x0f, 0xe1, 0x80, 0x63, 0xc2, 0x32, 0x7f, 0x67, 0x85, 0x15,
0x3d, 0x0d, 0xaa, 0xff, 0x58, 0xc4, 0x3c, 0x44, 0xf2, 0xb8, 0x5f, 0x7a,
0x8b, 0x98, 0x50, 0x6d, 0x5a, 0xb1, 0x25, 0xef, 0xb9, 0x42, 0x83, 0x77,
0x90, 0xe5, 0x6f, 0x9e, 0xb9, 0x50, 0x72, 0x9a, 0xb8, 0xd4, 0x53, 0xff,
0xea, 0x82, 0x6f, 0x9a, 0x2b, 0xce, 0xeb, 0xeb, 0x7b, 0x22, 0x27, 0xf6,
0xd0, 0x5c, 0xba, 0x54, 0xfe, 0x33, 0x82, 0xc0, 0xca, 0x85, 0x42, 0xe5,
0xaa, 0xa9, 0xa3, 0x25, 0x15, 0x11, 0xae, 0x59, 0x14, 0xbf, 0x26, 0x39,
0x96, 0xc6, 0x2a, 0x72, 0xfe, 0x19, 0x13, 0x95, 0x2a, 0x19, 0xc3, 0x84,
0xd0, 0x4e, 0x98, 0x73, 0xfa, 0xe5, 0x2a, 0x82, 0x60, 0xed, 0x79, 0x33,
0x5b, 0x39, 0x92, 0x22, 0x5e, 0x54, 0xa1, 0x98, 0x26, 0x35, 0x38, 0x1d,
0x02, 0x20, 0x90, 0x12, 0x05, 0xb5, 0xb8, 0x6d, 0x6d, 0x80, 0x1e, 0x75,
0x0d, 0x27, 0xb0, 0x46, 0x2c, 0xdb, 0x12, 0xc1, 0x6e, 0x8c, 0x31, 0x8a,
0xe2, 0x09, 0xdd, 0x10, 0x60, 0x86, 0x00, 0x5a, 0x95, 0x42, 0x60, 0x10,
0xa0, 0xb8, 0x3a, 0x80, 0x0d, 0xd6, 0x81, 0xca, 0x9a, 0x80, 0x36, 0x64,
0x0c, 0xee, 0x0a, 0x8d, 0xb6, 0xd6, 0x98, 0xda, 0x52, 0x7b, 0x80, 0x82,
0x5e, 0x6b, 0xd6, 0xe3, 0xa4, 0xf5, 0xf7, 0xfe, 0x2e, 0xe4, 0x8a, 0x70,
0xa1, 0x20, 0xd5, 0x57, 0xcb, 0x3e
};
+357
View File
@@ -0,0 +1,357 @@
/* rbzip2_decode() against streams bzip2 itself wrote (rbzip2_fixtures.h:
* six tables, three blocks, runs of every length and octet), and
* against bz_write.h's, whose shapes are chosen here: every block size,
* two to six tables, selectors of every rank, all 256 octets, runs that
* reach the 255 the run coding holds and runs of rank 0 thousands long,
* blocks filled to the last octet. Each must decode octet for octet,
* into a buffer of its size and into one grown as it decodes, whether
* a block's walk runs from one end or from both, and block by block
* from input in two pieces split anywhere, as a ring buffer wraps.
* Then what must fail: every prefix of a stream, a block's CRC wrong,
* the stream's changed, a randomised block, a buffer one octet short,
* and a stream with octets flipped, which must fail or decode, never
* read or write out of bounds. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <encodings/rbzip2.h>
#include "bz_write.h"
#include "rbzip2_fixtures.h"
static int failures;
static uint32_t x;
static uint32_t next(void)
{
x = x * 1103515245u + 12345u;
return x;
}
static void fail(const char *what, unsigned i)
{
printf("FAIL: %s (%u)\n", what, i);
failures++;
}
/* the fixtures' plaintexts, as fx.py made them */
static size_t gen_a(uint8_t *d)
{
size_t i;
x = 1;
for (i = 0; i < 3000; i++)
d[i] = (uint8_t)('a' + ((next() >> 16) % 16));
return 3000;
}
static size_t gen_b(uint8_t *d)
{
static const char p[] = "the quick brown fox jumps over the lazy dog ";
size_t i;
x = 1;
for (i = 0; i < 250000; i++)
{
d[i] = (uint8_t)p[i % (sizeof(p) - 1)];
if (i % 997 == 0)
d[i] = (uint8_t)('A' + ((next() >> 16) % 26));
}
return 250000;
}
static size_t gen_c(uint8_t *d)
{
size_t n = 0;
x = 1;
while (n < 60000)
{
uint32_t v = next();
size_t r = 1 + (v >> 4) % 600;
if (r > 60000 - n)
r = 60000 - n;
memset(d + n, (int)((v >> 16) & 0xff), r);
n += r;
}
return n;
}
/* into a buffer of the size, and into one grown as it goes */
static void decodes(rbzip2_t *bz, const uint8_t *s, size_t sn,
const uint8_t *want, size_t n, uint8_t *out, const char *what,
unsigned i)
{
uint8_t *grown = NULL;
size_t w = 0;
if ( rbzip2_decode(bz, out, n, s, sn, &w) != RBZIP2_OK
|| w != n || memcmp(out, want, n))
fail(what, i);
if ( rbzip2_decode_alloc(bz, s, sn, &grown, &w) != RBZIP2_OK
|| w != n || memcmp(grown, want, n))
fail(what, i + 10000);
else
grown[n] = 0; /* the octet to spare */
free(grown);
}
static uint8_t *copy_of(const uint8_t *s, size_t n)
{
uint8_t *c = (uint8_t*)malloc(n ? n : 1);
if (c && n)
memcpy(c, s, n);
return c;
}
/* @s handed over in two pieces each its own buffer, as a ring buffer
* wraps: for even @i split anew before every block from where the
* decoder has taken it to, for odd the same two throughout; the blocks
* must be @want's */
static void ring_decodes(rbzip2_t *bz, const uint8_t *s, size_t sn,
const uint8_t *want, size_t n, unsigned i)
{
size_t k = 4 + next() % (sn - 3);
uint8_t *a = copy_of(s, k);
uint8_t *b = copy_of(s + k, sn - k);
const uint8_t *blk;
size_t len, at = 0;
int e;
if (rbzip2_begin_ring(bz, a, k, b, sn - k) != RBZIP2_OK)
{
fail("a stream begun in two pieces", i);
free(a);
free(b);
return;
}
while ((e = rbzip2_step_block(bz, &blk, &len)) == RBZIP2_PENDING)
{
uint64_t t = rbzip2_taken(bz);
if (!len || len > n - at || memcmp(blk, want + at, len) || t > sn)
{
fail("a block from two pieces", i);
break;
}
at += len;
if (i & 1)
continue; /* the same two pieces all through */
free(a);
free(b);
k = (size_t)t + next() % (sn - (size_t)t + 1);
a = copy_of(s + t, k - (size_t)t);
b = copy_of(s + k, sn - k);
rbzip2_input(bz, a, k - (size_t)t, b, sn - k);
}
if (e != RBZIP2_OK || at != n || rbzip2_taken(bz) > sn)
fail("a stream from two pieces", i);
free(a);
free(b);
}
/* plaintext shape @k, @n octets */
static void shape(uint8_t *d, size_t n, unsigned k)
{
size_t i = 0;
switch (k % 6)
{
case 0: /* every octet, no order */
for (; i < n; i++)
d[i] = (uint8_t)(next() >> 16);
break;
case 1: /* a few letters */
for (; i < n; i++)
d[i] = (uint8_t)('a' + (next() >> 16) % 3);
break;
case 2: /* runs, short and up past 255 */
while (i < n)
{
uint32_t v = next();
size_t r = 1 + (v >> 3) % ((k & 8) ? 700 : 9);
for (; r && i < n; r--)
d[i++] = (uint8_t)(v >> 24);
}
break;
case 3: /* one octet throughout */
memset(d, 0x5a, n);
break;
case 4: /* a phrase over and over: rank 0 for thousands */
for (; i < n; i++)
d[i] = (uint8_t)"repetition"[i % 10];
break;
default: /* counting, then noise */
for (; i < n; i++)
d[i] = (uint8_t)(i < n / 2 ? i : next() >> 20);
break;
}
}
int main(void)
{
static uint8_t d[400000], s[460000], o[400016];
rbzip2_t *bz = rbzip2_new();
size_t n, sn, w;
unsigned i;
if (!bz)
return 2;
n = gen_a(d);
decodes(bz, fx_a, sizeof(fx_a), d, n, o, "bzip2's stream A", 0);
n = gen_b(d);
decodes(bz, fx_b, sizeof(fx_b), d, n, o, "bzip2's stream B", 0);
n = gen_c(d);
decodes(bz, fx_c, sizeof(fx_c), d, n, o, "bzip2's stream C", 0);
/* nothing at all */
sn = bz_write(s, sizeof(s), d, 0, 9, 1);
if (rbzip2_decode(bz, o, 0, s, sn, &w) != RBZIP2_OK || w)
fail("an empty stream", 0);
x = 99;
for (i = 0; i < 96; i++)
{
unsigned level = 1 + i % 9;
n = (i % 4 == 0) ? 100000u * (i % 3 ? 1 : 3) + i
: (i % 4 == 1) ? 1 + next() % 300 : next() % 40000;
if (level < 3 && i % 5 == 0)
n = 100000u * level - 19; /* exactly one full block */
shape(d, n, i);
sn = bz_write(s, sizeof(s), d, n, level, i);
if (!sn)
{
fail("the writer had no room", i);
continue;
}
decodes(bz, s, sn, d, n, o, "a written stream", i);
if (n && rbzip2_decode(bz, o, n - 1, s, sn, &w) != RBZIP2_ERROR_SPACE)
fail("a buffer one octet short", i);
/* what follows a stream is not read */
s[sn] = 0x42;
decodes(bz, s, sn + 1, d, n, o, "a stream with more after it", i);
}
/* a block at a time: what each step has written is final, and
* the last step is the stream's end */
shape(d, 350000, 7);
sn = bz_write(s, sizeof(s), d, 350000, 1, 7);
if (rbzip2_begin(bz, o, 350000, s, sn) != RBZIP2_OK)
fail("a stream begun", 0);
else
{
size_t last = 0;
unsigned steps = 0;
int e;
while ((e = rbzip2_step(bz, &w)) == RBZIP2_PENDING)
{
if (w <= last || w > 350000 || memcmp(o, d, w))
fail("a step's blocks", steps);
last = w;
steps++;
}
if (e != RBZIP2_OK || w != 350000 || steps < 4)
fail("a stream a block at a time", steps);
}
/* each block where the decoder leaves it, one after another the
* stream's octets */
if (rbzip2_begin_blocks(bz, s, sn) != RBZIP2_OK)
fail("a stream begun a block at a time", 0);
else
{
const uint8_t *blk;
size_t len, at = 0;
unsigned blocks = 0;
int e;
while ((e = rbzip2_step_block(bz, &blk, &len)) == RBZIP2_PENDING)
{
if (!len || len > 350000 - at || memcmp(blk, d + at, len))
fail("a block where the decoder leaves it", blocks);
at += len;
blocks++;
}
if (e != RBZIP2_OK || at != 350000 || blocks < 4)
fail("a stream's blocks one after another", blocks);
}
for (i = 0; i < 40; i++)
ring_decodes(bz, s, sn, d, 350000, i);
/* half of it given, as if the rest were still to be read: a block
* past the half fails, taking nothing more */
{
uint8_t *h = copy_of(s, sn / 2);
const uint8_t *blk;
size_t len;
int e;
if (rbzip2_begin_ring(bz, h, sn / 2, NULL, 0) == RBZIP2_OK)
{
while ((e = rbzip2_step_block(bz, &blk, &len)) == RBZIP2_PENDING) { }
if (e != RBZIP2_ERROR_DATA)
fail("half a stream in a ring", 0);
}
else
fail("half a stream begun", 0);
free(h);
}
if (rbzip2_begin(bz, o, 349999, s, sn) == RBZIP2_OK)
{
int e;
while ((e = rbzip2_step(bz, &w)) == RBZIP2_PENDING) { }
if (e != RBZIP2_ERROR_SPACE)
fail("a block at a time, a buffer one octet short", 0);
}
/* what must fail */
n = gen_a(d);
for (i = 0; i < sizeof(fx_a); i++)
if (rbzip2_decode(bz, o, n, fx_a, i, &w) == RBZIP2_OK)
fail("a prefix of a stream", i);
/* a block's CRC wrong, and the stream's made from it */
bzw_lie = 1;
sn = bz_write(s, sizeof(s), d, n, 9, 3);
bzw_lie = 0;
if (rbzip2_decode(bz, o, n, s, sn, &w) != RBZIP2_ERROR_DATA)
fail("a block CRC wrong", 0);
if (rbzip2_begin(bz, o, n, s, sn) == RBZIP2_OK)
{
int e;
while ((e = rbzip2_step(bz, &w)) == RBZIP2_PENDING) { }
if (e != RBZIP2_ERROR_DATA)
fail("a block CRC wrong, a block at a time", 0);
}
if (rbzip2_begin_blocks(bz, s, sn) == RBZIP2_OK)
{
const uint8_t *blk;
size_t len;
int e;
while ((e = rbzip2_step_block(bz, &blk, &len)) == RBZIP2_PENDING) { }
if (e != RBZIP2_ERROR_DATA)
fail("a block CRC wrong, where the decoder leaves it", 0);
}
memcpy(s, fx_a, sizeof(fx_a));
s[sizeof(fx_a) - 2] ^= 0x10; /* the stream's */
if (rbzip2_decode(bz, o, n, s, sizeof(fx_a), &w) != RBZIP2_ERROR_DATA)
fail("the stream CRC changed", 0);
memcpy(s, fx_a, sizeof(fx_a));
s[14] |= 0x80; /* the randomised bit */
if (rbzip2_decode(bz, o, n, s, sizeof(fx_a), &w) != RBZIP2_ERROR_DATA)
fail("a randomised block", 0);
if (rbzip2_decode(bz, o, n, (const uint8_t*)"BZh0", 4, &w) != RBZIP2_ERROR_DATA)
fail("block size 0", 0);
for (i = 0; i < 3000; i++)
{
const uint8_t *f = i % 3 == 0 ? fx_a : i % 3 == 1 ? fx_b : fx_c;
size_t fn = i % 3 == 0 ? sizeof(fx_a) : i % 3 == 1 ? sizeof(fx_b)
: sizeof(fx_c);
unsigned k, flips = 1 + next() % 4;
memcpy(s, f, fn);
for (k = 0; k < flips; k++)
s[4 + next() % (fn - 4)] ^= (uint8_t)(1u << (next() % 8));
rbzip2_decode(bz, o, sizeof(o), s, fn, &w);
}
rbzip2_free(bz);
if (failures)
{
printf("%d failure(s)\n", failures);
return 1;
}
printf("rbzip2_test: every stream decoded, whole and a block at a time, every bad one refused\n");
return 0;
}
+40
View File
@@ -0,0 +1,40 @@
include ../../sample_objdir.mk
TARGET := rzstd_raw_block_test
LIBRETRO_COMM_DIR := ../../..
SOURCES := \
rzstd_raw_block_test.c \
$(LIBRETRO_COMM_DIR)/encodings/encoding_rzstd.c \
$(LIBRETRO_COMM_DIR)/encodings/encoding_xxh64.c
CFLAGS += -Wall -pedantic -std=gnu99 -g1 -O2 -I$(LIBRETRO_COMM_DIR)/include
OBJS := $(call sample_objs,$(SOURCES:.c=.o))
ifneq ($(SANITIZER),)
CFLAGS := -fsanitize=$(SANITIZER) -fno-omit-frame-pointer $(CFLAGS)
LDFLAGS := -fsanitize=$(SANITIZER) $(LDFLAGS)
endif
all: $(TARGET)
%.o: %.c
$(CC) -c -o $@ $< $(CFLAGS)
$(SAMPLE_OBJDIR)/%.o: $$(call sample_src,$$*).c
@mkdir -p $(@D)
$(CC) -c -o $@ $< $(CFLAGS)
$(TARGET): $(OBJS)
$(CC) -o $@ $^ $(LDFLAGS)
check: $(TARGET)
./$(TARGET)
clean:
rm -rf $(SAMPLE_OBJDIR)
rm -f $(TARGET) $(OBJS)
.PHONY: all check clean
@@ -0,0 +1,174 @@
/* A block the encoder searched and then stored raw, because its
* matches did not pay for themselves, must leave the recent offsets as
* the decoder sees them: a raw block does not move them. The search
* had already moved them, so the next compressed block named an offset
* by a repeat code the decoder resolved to a different one, and the
* frame decoded - by this decoder and by the reference - to the wrong
* octets, with no error.
*
* Three 128 KiB blocks: one whose last match is four back; one of
* noise holding a copy from a thousand back, not worth compressing;
* one repeating every thousand octets. At every level the frame must
* come back as it went in, whole and handed over in the pieces
* rzstd_blocks_want() asks for, and a piece short is refused. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <encodings/rzstd.h>
#define BLK 0x20000
static unsigned seed = 7;
/* @z, @len octets, handed to rzstd_blocks_give() as it asks for them:
* 1 when it comes out as @want, and is all there is */
static int pieces(const uint8_t *z, size_t len, const uint8_t *want,
size_t n)
{
rzstd_blocks_t *b = rzstd_blocks_open();
size_t at = 0, out = 0, w, got;
const uint8_t *d;
int ok = b != NULL;
while (ok && (w = rzstd_blocks_want(b)))
{
if (w > RZSTD_WANT_MAX || at + w > len
|| rzstd_blocks_give(b, z + at, w, &d, &got)
!= RZSTD_PROCESS_NEXT
|| out + got > n || (got && memcmp(d, want + out, got)))
ok = 0;
at += w;
out += got;
}
rzstd_blocks_free(b);
return ok && at == len && out == n;
}
static uint8_t rnd(void)
{
seed = seed * 1103515245u + 12345u;
return (uint8_t)(seed >> 19);
}
/* @z, @len octets, through rzstd_blocks_begin(): 1 when it comes out
* as @want, @n octets */
static int stepped(const uint8_t *z, size_t len, const uint8_t *want,
size_t n)
{
rzstd_blocks_t *b = rzstd_blocks_begin(z, len);
size_t out = 0, got;
const uint8_t *d;
int e = RZSTD_PROCESS_ERROR;
int ok = b != NULL;
while (ok && (e = rzstd_blocks_step(b, &d, &got)) == RZSTD_PROCESS_NEXT)
{
if (out + got > n || (got && memcmp(d, want + out, got)))
ok = 0;
out += got;
}
rzstd_blocks_free(b);
return ok && e == RZSTD_PROCESS_END && out == n;
}
/* Many megabytes in a window of 2^@log: a single-segment frame of
* rzstd_encode()'s, its header made one with a window descriptor and
* no size, so that decoding a block at a time goes round the ring many
* times, with matches across where it comes round */
static int ring(const uint8_t *src, size_t n, uint8_t *z, size_t cap,
unsigned log)
{
size_t wrote = 0;
if ( rzstd_encode(z, cap, src, n, 9, &wrote) != RZSTD_PROCESS_END
|| wrote < 9 || z[4] != 0xa0)
return 0;
/* magic, descriptor 0, window, then the blocks after the old
* header's four-octet size */
z[4] = 0;
z[5] = (uint8_t)((log - 10) << 3);
memmove(z + 6, z + 9, wrote - 9);
wrote -= 3;
return stepped(z, wrote, src, n) && pieces(z, wrote, src, n);
}
int main(void)
{
size_t n = 3 * BLK, i, cap = rzstd_compress_bound(3 * BLK), wrote, got;
uint8_t *src = (uint8_t*)malloc(n), *z = (uint8_t*)malloc(cap);
uint8_t *out = (uint8_t*)malloc(n);
int level, fails = 0;
/* a period of four to the end of the first block */
for (i = 0; i < BLK; i++)
src[i] = i < 4 ? rnd() : src[i - 4];
/* noise, early in which eight octets from a thousand back: a
* match the search takes, too short to pay for the block */
for (i = BLK; i < 2 * BLK; i++)
src[i] = rnd();
memcpy(src + BLK + 1001, src + BLK + 1, 8);
/* a thousand octets of noise, then repeated */
for (i = 2 * BLK; i < 3 * BLK; i++)
src[i] = i < 2 * BLK + 1000 ? rnd() : src[i - 1000];
for (level = 1; level <= 9; level++)
{
wrote = got = 0;
if ( rzstd_encode(z, cap, src, n, level, &wrote) != RZSTD_PROCESS_END
|| rzstd_decode(out, n, z, wrote, &got) != RZSTD_PROCESS_END
|| got != n || memcmp(out, src, n))
{
for (i = 0; i < got && out[i] == src[i]; i++) ;
fprintf(stderr, "FAIL: level %d: decoded %lu, first difference at %lu\n",
level, (unsigned long)got, (unsigned long)i);
fails++;
}
if (!pieces(z, wrote, src, n))
{
fprintf(stderr, "FAIL: level %d: not the same in pieces\n", level);
fails++;
}
if (pieces(z, wrote - 1, src, n))
{
fprintf(stderr, "FAIL: level %d: a piece short and still whole\n",
level);
fails++;
}
}
free(src);
free(z);
free(out);
/* six megabytes of few letters, with runs copied from up to 120 KiB
* back */
n = 6 * 1024 * 1024;
cap = rzstd_compress_bound(n);
src = (uint8_t*)malloc(n);
z = (uint8_t*)malloc(cap);
for (i = 0; i < n; )
{
size_t run = 64 + rnd() * 8;
size_t back = 1024 + ((size_t)rnd() << 9);
if (i > back && (rnd() & 1))
for (; run && i < n; run--, i++)
src[i] = src[i - back];
else
for (; run && i < n; run--, i++)
src[i] = (uint8_t)('a' + (rnd() & 3));
}
if (!ring(src, n, z, cap, 17))
{
fprintf(stderr, "FAIL: a window of 128 KiB, a block at a time\n");
fails++;
}
if (!ring(src, n, z, cap, 20))
{
fprintf(stderr, "FAIL: a window of 1 MiB, a block at a time\n");
fails++;
}
free(src);
free(z);
if (fails)
return 1;
printf("ok: a raw block between compressed ones, levels 1 to 9, whole and in pieces; a ring gone round\n");
return 0;
}
+44
View File
@@ -0,0 +1,44 @@
include ../../sample_objdir.mk
TARGET := xxhash_test
LIBRETRO_COMM_DIR := ../../..
# The test includes encoding_xxh3.c, to reach each of its paths;
# xxh3_64() asks cpu_features_get() which one this CPU takes.
SOURCES := \
xxhash_test.c \
$(LIBRETRO_COMM_DIR)/encodings/encoding_xxh64.c \
$(LIBRETRO_COMM_DIR)/features/features_cpu.c \
$(LIBRETRO_COMM_DIR)/compat/compat_strl.c \
$(LIBRETRO_COMM_DIR)/time/rtime.c
CFLAGS += -Wall -pedantic -std=gnu99 -g1 -O2 -I$(LIBRETRO_COMM_DIR)/include
OBJS := $(call sample_objs,$(SOURCES:.c=.o))
ifneq ($(SANITIZER),)
CFLAGS := -fsanitize=$(SANITIZER) -fno-omit-frame-pointer $(CFLAGS)
LDFLAGS := -fsanitize=$(SANITIZER) $(LDFLAGS)
endif
all: $(TARGET)
%.o: %.c ../../../encodings/encoding_xxh3.c
$(CC) -c -o $@ $< $(CFLAGS)
$(SAMPLE_OBJDIR)/%.o: $$(call sample_src,$$*).c
@mkdir -p $(@D)
$(CC) -c -o $@ $< $(CFLAGS)
$(TARGET): $(OBJS)
$(CC) -o $@ $^ $(LDFLAGS)
check: $(TARGET)
./$(TARGET)
clean:
rm -rf $(SAMPLE_OBJDIR)
rm -f $(TARGET) $(OBJS)
.PHONY: all check clean
+382
View File
@@ -0,0 +1,382 @@
/* Copyright (C) 2010-2026 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (xxhash_test.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* XXH64 and XXH3 (64-bit) against xxHash's own values: the ones its
* sanity check holds, over the buffer it fills, and more made with
* xxHash 0.8.1 over the same buffer, longer, for the lengths around
* each of XXH3's turns and for many blocks. XXH64 is taken in one call
* and in pieces of every size; XXH3's long inputs go through each path
* this CPU can run, which must all agree with each other at every
* length up to three blocks and at every alignment. XXH3 taken in
* pieces must be what it is in one call, through each path, for the
* vectors and for every length to three blocks, split anywhere. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <encodings/xxh64.h>
/* the paths themselves, which xxh3_64() chooses between */
#include "../../../encodings/encoding_xxh3.c"
#define PRIME32 2654435761U
#define PRIME64 11400714785074694797ULL
#define BUF_LEN 100000
typedef struct
{
uint32_t len;
uint64_t seed;
uint64_t hash;
} vec_t;
/* XSUM_XXH64_testdata */
static const vec_t xxh64_spec[] = {
{ 0, 0, 0xEF46DB3751D8E999ULL },
{ 0, PRIME32, 0xAC75FDA2929B17EFULL },
{ 1, 0, 0xE934A84ADB052768ULL },
{ 1, PRIME32, 0x5014607643A9B4C3ULL },
{ 4, 0, 0x9136A0DCA57457EEULL },
{ 14, 0, 0x8282DCC4994E35C8ULL },
{ 14, PRIME32, 0xC3BD6BF63DEB6DF0ULL },
{ 222, 0, 0xB641AE8CB691C174ULL },
{ 222, PRIME32, 0x20CB8AB7AE10C14AULL },
{ 31, 0x0000000000000000ULL, 0x299B39A290E6D783ULL },
{ 31, 0x9E3779B185EBCA8DULL, 0x51AAF1A336575F00ULL },
{ 31, 0x0123456789ABCDEFULL, 0x15DF0C5F454CB442ULL },
{ 32, 0x0000000000000000ULL, 0x18B216492BB44B70ULL },
{ 32, 0x9E3779B185EBCA8DULL, 0x21D817283F4B6283ULL },
{ 32, 0x0123456789ABCDEFULL, 0x82335D2BA9AFBF4EULL },
{ 33, 0x0000000000000000ULL, 0x55C8DC3E578F5B59ULL },
{ 33, 0x9E3779B185EBCA8DULL, 0xB09782549294DF85ULL },
{ 33, 0x0123456789ABCDEFULL, 0x28F7AA5CD4F51D46ULL },
{ 63, 0x0000000000000000ULL, 0xA9EFBE0FA0F3F4E7ULL },
{ 63, 0x9E3779B185EBCA8DULL, 0x34C3933DC0040446ULL },
{ 63, 0x0123456789ABCDEFULL, 0x9F2E7DD211D5FD08ULL },
{ 64, 0x0000000000000000ULL, 0xEF558F8ACAC2B5CDULL },
{ 64, 0x9E3779B185EBCA8DULL, 0xF90D26FED8023D61ULL },
{ 64, 0x0123456789ABCDEFULL, 0x9406ECC32D7852ECULL },
{ 65, 0x0000000000000000ULL, 0xDE0F20DC2631AF7AULL },
{ 65, 0x9E3779B185EBCA8DULL, 0x7814174CD6405BEEULL },
{ 65, 0x0123456789ABCDEFULL, 0x94E74DEC4157C995ULL },
{ 100, 0x0000000000000000ULL, 0x4BFE019CD91D9EA4ULL },
{ 100, 0x9E3779B185EBCA8DULL, 0x38F1D4AABFD12D0FULL },
{ 100, 0x0123456789ABCDEFULL, 0x70F40596D4A2F05BULL },
{ 1000, 0x0000000000000000ULL, 0x52BD1358F22E9EF7ULL },
{ 1000, 0x9E3779B185EBCA8DULL, 0x3FE6446814FAE5EDULL },
{ 1000, 0x0123456789ABCDEFULL, 0x6FEBFAD7085386E5ULL },
{ 4097, 0x0000000000000000ULL, 0xF617DE30F1C0ABB6ULL },
{ 4097, 0x9E3779B185EBCA8DULL, 0x7A4E1FBFDD1DDED1ULL },
{ 4097, 0x0123456789ABCDEFULL, 0x0EC301EA1B060486ULL },
{ 100000, 0x0000000000000000ULL, 0x2F2257F45994FF6AULL },
{ 100000, 0x9E3779B185EBCA8DULL, 0xA53C361A45679B06ULL },
{ 100000, 0x0123456789ABCDEFULL, 0x19A6D33D882D5112ULL },
};
/* XSUM_XXH3_testdata */
static const vec_t xxh3_spec[] = {
{ 0, 0, 0x2D06800538D394C2ULL },
{ 0, PRIME64, 0xA8A6B918B2F0364AULL },
{ 1, 0, 0xC44BDFF4074EECDBULL },
{ 1, PRIME64, 0x032BE332DD766EF8ULL },
{ 6, 0, 0x27B56A84CD2D7325ULL },
{ 6, PRIME64, 0x84589C116AB59AB9ULL },
{ 12, 0, 0xA713DAF0DFBB77E7ULL },
{ 12, PRIME64, 0xE7303E1B2336DE0EULL },
{ 24, 0, 0xA3FE70BF9D3510EBULL },
{ 24, PRIME64, 0x850E80FC35BDD690ULL },
{ 48, 0, 0x397DA259ECBA1F11ULL },
{ 48, PRIME64, 0xADC2CBAA44ACC616ULL },
{ 80, 0, 0xBCDEFBBB2C47C90AULL },
{ 80, PRIME64, 0xC6DD0CB699532E73ULL },
{ 195, 0, 0xCD94217EE362EC3AULL },
{ 195, PRIME64, 0xBA68003D370CB3D9ULL },
{ 403, 0, 0xCDEB804D65C6DEA4ULL },
{ 403, PRIME64, 0x6259F6ECFD6443FDULL },
{ 512, 0, 0x617E49599013CB6BULL },
{ 512, PRIME64, 0x3CE457DE14C27708ULL },
{ 2048, 0, 0xDD59E2C3A5F038E0ULL },
{ 2048, PRIME64, 0x66F81670669ABABCULL },
{ 2240, 0, 0x6E73A90539CF2948ULL },
{ 2240, PRIME64, 0x757BA8487D1B5247ULL },
{ 2367, 0, 0xCB37AEB9E5D361EDULL },
{ 2367, PRIME64, 0xD2DB3415B942B42AULL },
{ 129, 0x0000000000000000ULL, 0x98F1B0A679A2CA29ULL },
{ 129, 0x9E3779B185EBCA8DULL, 0x21FFFDBCA099C844ULL },
{ 129, 0x0123456789ABCDEFULL, 0x028E1AF162B1D2A9ULL },
{ 240, 0x0000000000000000ULL, 0x81C3C2B67F568CCFULL },
{ 240, 0x9E3779B185EBCA8DULL, 0xCC0F58C27EF3D8EEULL },
{ 240, 0x0123456789ABCDEFULL, 0xFF9A5EA1F3E336C1ULL },
{ 241, 0x0000000000000000ULL, 0xC5A639ECD2030E5EULL },
{ 241, 0x9E3779B185EBCA8DULL, 0xDDA9B0A161D4829AULL },
{ 241, 0x0123456789ABCDEFULL, 0x39D4913D191D5F3DULL },
{ 255, 0x0000000000000000ULL, 0xE98F979F4ED8A197ULL },
{ 255, 0x9E3779B185EBCA8DULL, 0x2ACA7901D9538C75ULL },
{ 255, 0x0123456789ABCDEFULL, 0x028E72AF5FFADEA6ULL },
{ 256, 0x0000000000000000ULL, 0x55DE574AD89D0AC5ULL },
{ 256, 0x9E3779B185EBCA8DULL, 0x4D30234B7A3AA61CULL },
{ 256, 0x0123456789ABCDEFULL, 0x1D48B7293297B67EULL },
{ 1023, 0x0000000000000000ULL, 0x87A8F7B2F2E22496ULL },
{ 1023, 0x9E3779B185EBCA8DULL, 0x0F0F02DE8590E1B5ULL },
{ 1023, 0x0123456789ABCDEFULL, 0x4AF7B4E4D28BE1B5ULL },
{ 1024, 0x0000000000000000ULL, 0xDD85C9B5C1109C5CULL },
{ 1024, 0x9E3779B185EBCA8DULL, 0xEF368A8A2EBABAEFULL },
{ 1024, 0x0123456789ABCDEFULL, 0x409D0F8806FD35EBULL },
{ 1025, 0x0000000000000000ULL, 0xD870C0FA13211C6AULL },
{ 1025, 0x9E3779B185EBCA8DULL, 0x96792BCF9AF88519ULL },
{ 1025, 0x0123456789ABCDEFULL, 0xB09ED51FDD614F0FULL },
{ 1088, 0x0000000000000000ULL, 0xCC1450EA6B52A8F4ULL },
{ 1088, 0x9E3779B185EBCA8DULL, 0x542632D046F3DDE4ULL },
{ 1088, 0x0123456789ABCDEFULL, 0x2B7F77D992CF45C0ULL },
{ 4103, 0x0000000000000000ULL, 0xD6E778C94923EA44ULL },
{ 4103, 0x9E3779B185EBCA8DULL, 0x917A70D1756179ADULL },
{ 4103, 0x0123456789ABCDEFULL, 0x7E60E72087D382EBULL },
{ 16384, 0x0000000000000000ULL, 0xEDD17B079A94769EULL },
{ 16384, 0x9E3779B185EBCA8DULL, 0x53DD5B3F9786A823ULL },
{ 16384, 0x0123456789ABCDEFULL, 0xA73A83F142BDF3C2ULL },
{ 100000, 0x0000000000000000ULL, 0x34D658192A014311ULL },
{ 100000, 0x9E3779B185EBCA8DULL, 0x0682260A8A5AFE82ULL },
{ 100000, 0x0123456789ABCDEFULL, 0xF39E05B863A8BB36ULL },
};
typedef uint64_t (*path_fn)(const uint8_t *in, size_t len, uint64_t seed);
typedef struct
{
const char *name;
path_fn fn;
int id; /* xxh3_path for it */
} path_t;
static unsigned failures;
static unsigned checks;
static uint64_t rng = 88172645463325252ULL;
static size_t next_rand(size_t below)
{
rng ^= rng << 13;
rng ^= rng >> 7;
rng ^= rng << 17;
return (size_t)(rng % below);
}
/* @len octets at @in through an xxh3_state_t in pieces: of @step each,
* or, with @step 0, of random sizes up to two blocks */
static uint64_t xxh3_pieces(const uint8_t *in, size_t len, uint64_t seed,
size_t step)
{
static xxh3_state_t st;
size_t at = 0;
xxh3_reset(&st, seed);
xxh3_update(&st, in, 0);
while (at < len)
{
size_t k = step ? step : 1 + next_rand(2 * 1024 + 70);
if (k > len - at)
k = len - at;
xxh3_update(&st, in + at, k);
at += k;
}
return xxh3_digest(&st);
}
static void fail(const char *what, unsigned len, uint64_t seed,
uint64_t got, uint64_t want)
{
if (failures++ < 20)
printf("[FAIL] %s, %u octets, seed %016llX: %016llX, not %016llX\n",
what, len, (unsigned long long)seed, (unsigned long long)got,
(unsigned long long)want);
}
int main(void)
{
uint8_t *buf = (uint8_t*)malloc(BUF_LEN + 8);
path_t paths[5];
unsigned np = 0, i, p;
uint64_t g = PRIME32;
size_t n;
if (!buf)
return 2;
/* XSUM_fillTestBuffer */
for (n = 0; n < BUF_LEN; n++)
{
buf[n] = (uint8_t)(g >> 56);
g *= PRIME64;
}
paths[np].name = "scalar";
paths[np].id = 1;
paths[np++].fn = xxh3_long_scalar;
#if defined(XXH3_HAVE_SSE2)
{
uint64_t simd = cpu_features_get();
if (simd & RETRO_SIMD_SSE2)
{
paths[np].name = "SSE2";
paths[np].id = 2;
paths[np++].fn = xxh3_long_sse2;
}
#if defined(XXH3_HAVE_AVX2)
if (simd & RETRO_SIMD_AVX2)
{
paths[np].name = "AVX2";
paths[np].id = 3;
paths[np++].fn = xxh3_long_avx2;
}
#endif
#if defined(XXH3_HAVE_AVX512)
if (simd & RETRO_SIMD_AVX512)
{
paths[np].name = "AVX-512";
paths[np].id = 4;
paths[np++].fn = xxh3_long_avx512;
}
#endif
}
#elif defined(XXH3_HAVE_NEON)
paths[np].name = "NEON";
paths[np].id = 0;
paths[np++].fn = xxh3_long_neon;
#endif
/* XXH64: whole, and in pieces */
for (i = 0; i < sizeof(xxh64_spec) / sizeof(xxh64_spec[0]); i++)
{
const vec_t *v = &xxh64_spec[i];
uint64_t h = xxh64(buf, v->len, v->seed);
xxh64_state_t s;
size_t at;
checks++;
if (h != v->hash)
fail("xxh64", v->len, v->seed, h, v->hash);
for (n = 1; n <= 97; n += 3)
{
xxh64_reset(&s, v->seed);
for (at = 0; at < v->len; at += n)
xxh64_update(&s, buf + at, at + n > v->len ? v->len - at : n);
checks++;
if ((h = xxh64_digest(&s)) != v->hash)
fail("xxh64 in pieces", v->len, v->seed, h, v->hash);
}
}
/* XXH3: the chosen path, then each */
for (i = 0; i < sizeof(xxh3_spec) / sizeof(xxh3_spec[0]); i++)
{
const vec_t *v = &xxh3_spec[i];
uint64_t h = xxh3_64(buf, v->len, v->seed);
checks++;
if (h != v->hash)
fail("xxh3_64", v->len, v->seed, h, v->hash);
if (v->len <= 240)
continue;
for (p = 0; p < np; p++)
{
checks++;
if ((h = paths[p].fn(buf, v->len, v->seed)) != v->hash)
fail(paths[p].name, v->len, v->seed, h, v->hash);
}
}
/* every length to three blocks, at every alignment, every path as
* the scalar one */
for (n = 241; n <= 3 * 1024 + 64; n++)
{
unsigned off = (unsigned)(n & 7);
uint64_t seed = (n & 1) ? 0 : (uint64_t)n * PRIME64;
uint64_t want = xxh3_long_scalar(buf + off, n, seed);
for (p = 1; p < np; p++)
{
uint64_t h = paths[p].fn(buf + off, n, seed);
checks++;
if (h != want)
fail(paths[p].name, (unsigned)n, seed, h, want);
}
}
/* XXH3 in pieces, through each path's blocks */
for (p = 0; p < np; p++)
{
static const size_t steps[] = { 1, 7, 63, 64, 65, 100, 240, 241,
255, 256, 257, 1000, 1023, 1024, 1025, 3000, 0 };
static const size_t after[] = { 7, 63, 64, 65, 255, 256, 257, 300,
1000 };
unsigned lead;
#if defined(XXH3_HAVE_SSE2)
retro_atomic_store_release_int(&xxh3_path, paths[p].id);
#endif
for (i = 0; i < sizeof(xxh3_spec) / sizeof(xxh3_spec[0]); i++)
{
const vec_t *v = &xxh3_spec[i];
unsigned k;
for (k = 0; k < sizeof(steps) / sizeof(steps[0]); k++)
{
uint64_t h = xxh3_pieces(buf, v->len, v->seed, steps[k]);
checks++;
if (h != v->hash)
fail("xxh3 in pieces", v->len, v->seed, h, v->hash);
}
}
/* held octets ending at every place in a stripe, then pieces that
* are held after them, fill the stripe or go where they lie, and
* leave every count of octets over */
for (lead = 1; lead <= 64; lead++)
for (i = 0; i < sizeof(after) / sizeof(after[0]); i++)
{
static xxh3_state_t st;
uint64_t seed = (lead & 1) ? 0 : (uint64_t)lead * PRIME64;
size_t len = 5000 + lead, at, k;
uint64_t want = xxh3_64(buf, len, seed), h;
xxh3_reset(&st, seed);
xxh3_update(&st, buf, lead);
for (at = lead; at < len; at += k)
{
k = after[i] < len - at ? after[i] : len - at;
xxh3_update(&st, buf + at, k);
}
checks++;
if ((h = xxh3_digest(&st)) != want)
fail("xxh3 after a held lead", (unsigned)len, seed, h, want);
}
for (n = 0; n <= 3 * 1024 + 64; n++)
{
uint64_t seed = (n & 1) ? 0 : (uint64_t)n * PRIME64;
uint64_t want = xxh3_64(buf + (n & 7), n, seed);
uint64_t h = xxh3_pieces(buf + (n & 7), n, seed, 0);
checks++;
if (h != want)
fail("xxh3 split anywhere", (unsigned)n, seed, h, want);
}
}
free(buf);
printf("xxhash_test: XXH3 through");
for (p = 0; p < np; p++)
printf(" %s", paths[p].name);
printf("; %u checks, %u failed\n", checks, failures);
return failures ? 1 : 0;
}
+79
View File
@@ -0,0 +1,79 @@
include ../../sample_objdir.mk
TARGET := archive_7z_test
LIBRETRO_COMM_DIR := ../../..
# The 7z backend through the archive layer, mapped with MMAP=1 (the
# default) and read through the stream with MMAP=0.
SOURCES := \
archive_7z_test.c \
$(LIBRETRO_COMM_DIR)/compat/fopen_utf8.c \
$(LIBRETRO_COMM_DIR)/compat/compat_strl.c \
$(LIBRETRO_COMM_DIR)/compat/compat_strcasestr.c \
$(LIBRETRO_COMM_DIR)/compat/compat_posix_string.c \
$(LIBRETRO_COMM_DIR)/encodings/encoding_crc32.c \
$(LIBRETRO_COMM_DIR)/features/features_cpu.c \
$(LIBRETRO_COMM_DIR)/encodings/encoding_utf.c \
$(LIBRETRO_COMM_DIR)/file/archive_file.c \
$(LIBRETRO_COMM_DIR)/file/archive_file_zlib.c \
$(LIBRETRO_COMM_DIR)/file/archive_file_7z.c \
$(LIBRETRO_COMM_DIR)/formats/7z/r7z_archive.c \
$(LIBRETRO_COMM_DIR)/formats/7z/r7z_lzma.c \
$(LIBRETRO_COMM_DIR)/formats/7z/r7z_lzma_stream.c \
$(LIBRETRO_COMM_DIR)/formats/7z/r7z_lzma2.c \
$(LIBRETRO_COMM_DIR)/formats/7z/r7z_bcj2.c \
$(LIBRETRO_COMM_DIR)/formats/7z/r7z_filters.c \
$(LIBRETRO_COMM_DIR)/formats/rar/rrar_ppmd7.c \
$(LIBRETRO_COMM_DIR)/file/file_path.c \
$(LIBRETRO_COMM_DIR)/file/file_path_io.c \
$(LIBRETRO_COMM_DIR)/lists/string_list.c \
$(LIBRETRO_COMM_DIR)/streams/file_stream.c \
$(LIBRETRO_COMM_DIR)/string/rstrtod.c \
$(LIBRETRO_COMM_DIR)/string/stdstring.c \
$(LIBRETRO_COMM_DIR)/streams/trans_stream.c \
$(LIBRETRO_COMM_DIR)/streams/trans_stream_pipe.c \
$(LIBRETRO_COMM_DIR)/streams/trans_stream_deflate.c \
$(LIBRETRO_COMM_DIR)/encodings/encoding_deflate.c \
$(LIBRETRO_COMM_DIR)/vfs/vfs_implementation.c \
$(LIBRETRO_COMM_DIR)/time/rtime.c
OBJS := $(call sample_objs,$(SOURCES:.c=.o))
override CPPFLAGS += -DHAVE_COMPRESSION -DHAVE_7ZIP -I$(LIBRETRO_COMM_DIR)/include
CFLAGS += -Wall -pedantic -std=gnu99 -g1
# A folder of others over 64 KiB is never held whole, so the big ones
# here have their members streamed one after another
override CPPFLAGS += -DSEVENZIP_FOLDER_KEEP=65536
MMAP ?= 1
ifeq ($(MMAP),1)
override CPPFLAGS += -DHAVE_MMAP
endif
ifneq ($(SANITIZER),)
CFLAGS := -fsanitize=$(SANITIZER) -fno-omit-frame-pointer $(CFLAGS)
LDFLAGS := -fsanitize=$(SANITIZER) $(LDFLAGS)
endif
all: $(TARGET)
%.o: %.c Makefile
$(CC) -c -o $@ $< $(CPPFLAGS) $(CFLAGS)
$(SAMPLE_OBJDIR)/%.o: $$(call sample_src,$$*).c Makefile
@mkdir -p $(@D)
$(CC) -c -o $@ $< $(CPPFLAGS) $(CFLAGS)
$(TARGET): $(OBJS)
$(CC) -o $@ $^ $(LDFLAGS)
check: $(TARGET)
./$(TARGET)
clean:
rm -rf $(SAMPLE_OBJDIR)
rm -f $(TARGET) $(OBJS)
.PHONY: all check clean
File diff suppressed because it is too large. Load diff
+259
View File
@@ -0,0 +1,259 @@
/* Copyright (C) 2010-2026 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (archive_7z_test.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* The 7z backend through the archive layer: a solid LZMA archive, a
* stored one and a BCJ2 one 7-Zip wrote are listed, every member read
* whole with file_archive_compressed_read() and every member extracted
* through one transfer, solid members one after another from the
* folder held, or out of a folder over SEVENZIP_FOLDER_KEEP streamed,
* each going on from the one before. Built with HAVE_MMAP the archives
* are mapped, and the members handed out where the folder cache holds
* them; every member must come out as it went in either way. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <boolean.h>
#include <file/archive_file.h>
#include <file/file_path.h>
#include <lists/string_list.h>
#include <streams/file_stream.h>
#include "archive_7z_fixtures.h"
#include "../../formats/r7z/r7z_stored_fixtures.h"
#include "../../formats/r7z/r7z_bcj2_fixtures.h"
#define OUT_PREFIX "archive_7z_test_out_"
static unsigned failures;
struct member
{
const char *name;
size_t size;
int kind;
};
static void fail(const char *what, const char *member)
{
printf("[FAIL] %s: %s\n", what, member);
failures++;
}
/* r7z_bcj2_test.c's program-like octets */
static void program(uint8_t *d, size_t n)
{
static const uint8_t push[3] = { 0x55, 0x89, 0xe5 };
static const uint8_t jz[6] = { 0x0f, 0x84, 0x10, 0x00, 0x00, 0x00 };
static const uint8_t ret[4] = { 0x8b, 0x45, 0x08, 0xc3 };
uint32_t x = 1, j = 0;
size_t at = 0;
while (at < n)
{
uint8_t ins[6];
size_t len, k;
uint32_t kind;
j++;
kind = j % 5;
if (j % 61 == 0)
{
x = x * 1103515245u + 12345u;
kind = (x >> 16) % 5;
}
if (kind < 2)
{
uint32_t tgt = kind == 0 ? 0x1000u * ((j / 5) % 3) : 0x800u;
uint32_t rel = tgt - (uint32_t)(at + 5);
ins[0] = kind == 0 ? 0xe8 : 0xe9;
ins[1] = (uint8_t)rel;
ins[2] = (uint8_t)(rel >> 8);
ins[3] = (uint8_t)(rel >> 16);
ins[4] = (uint8_t)(rel >> 24);
len = 5;
}
else if (kind == 2)
{
memcpy(ins, push, 3);
len = 3;
}
else if (kind == 3)
{
memcpy(ins, jz, 6);
len = 6;
}
else
{
memcpy(ins, ret, 4);
len = 4;
}
for (k = 0; k < len && at < n; k++)
d[at++] = ins[k];
}
}
static uint8_t *expected(const struct member *m)
{
uint8_t *d = (uint8_t*)malloc(m->size ? m->size : 1);
size_t i;
for (i = 0; i < m->size; i++)
d[i] = (uint8_t)(m->kind == 0 ? ((i * 13) ^ (i >> 3))
: m->kind == 1 ? (i * 7 + (i >> 9)) : (i >> 4));
if (m->kind == 3)
program(d, m->size);
return d;
}
static int extract_cb(const char *name, const char *valid_exts,
const uint8_t *cdata, unsigned cmode, uint64_t csize, uint64_t size,
uint32_t checksum, struct archive_extract_userdata *userdata)
{
char out[256];
snprintf(out, sizeof(out), OUT_PREFIX "%s", name);
if (file_archive_perform_mode(out, valid_exts, cdata, cmode,
csize, size, checksum, userdata))
(*(unsigned*)userdata->cb_data)++;
return 1;
}
static void check(const char *path, const uint8_t *fx, size_t fx_len,
const struct member *m, unsigned n)
{
struct archive_extract_userdata userdata;
file_archive_transfer_t state;
struct string_list *list;
unsigned extracted = 0, i;
bool returnerr = true;
FILE *f = fopen(path, "wb");
if (!f || fwrite(fx, 1, fx_len, f) != fx_len)
fail("cannot write", path);
if (f)
fclose(f);
if (!(list = file_archive_get_file_list(path, NULL)) || list->size != n)
fail("listed", path);
string_list_free(list);
for (i = 0; i < n; i++)
{
char member[256];
void *buf = NULL;
int64_t len = 0;
uint8_t *want = expected(&m[i]);
snprintf(member, sizeof(member), "%s#%s", path, m[i].name);
if ( !file_archive_compressed_read(member, &buf, NULL, &len)
|| len != (int64_t)m[i].size || memcmp(buf, want, m[i].size)
|| ((char*)buf)[len] != '\0')
fail("read whole", member);
free(buf);
free(want);
}
memset(&state, 0, sizeof(state));
memset(&userdata, 0, sizeof(userdata));
state.type = ARCHIVE_TRANSFER_INIT;
userdata.cb_data = &extracted;
while (file_archive_parse_file_iterate(&state, &returnerr, path,
NULL, extract_cb, &userdata) == 0) { }
if (extracted != n)
fail("not every member extracted", path);
for (i = 0; i < n; i++)
{
char out[256];
void *got = NULL;
int64_t got_len = 0;
uint8_t *want = expected(&m[i]);
snprintf(out, sizeof(out), OUT_PREFIX "%s", m[i].name);
if ( !filestream_read_file(out, &got, &got_len)
|| got_len != (int64_t)m[i].size || memcmp(got, want, m[i].size))
fail("extracted", m[i].name);
free(got);
free(want);
remove(out);
}
remove(path);
}
/* @fx with an octet of its packed stream changed: big.bin is not left
* behind, whether that fails its CRC or its stream */
static void check_refused(const char *path, const uint8_t *fx, size_t fx_len,
size_t at)
{
struct archive_extract_userdata userdata;
file_archive_transfer_t state;
unsigned extracted = 0;
bool returnerr = true;
uint8_t *bad = (uint8_t*)malloc(fx_len);
FILE *f;
memcpy(bad, fx, fx_len);
bad[at] ^= 0x20;
if (!(f = fopen(path, "wb")) || fwrite(bad, 1, fx_len, f) != fx_len)
fail("cannot write", path);
if (f)
fclose(f);
memset(&state, 0, sizeof(state));
memset(&userdata, 0, sizeof(userdata));
state.type = ARCHIVE_TRANSFER_INIT;
userdata.cb_data = &extracted;
while (file_archive_parse_file_iterate(&state, &returnerr, path,
NULL, extract_cb, &userdata) == 0) { }
if (path_is_valid(OUT_PREFIX "big.bin"))
fail("left behind with its stream changed", path);
remove(OUT_PREFIX "a.bin");
remove(OUT_PREFIX "big.bin");
remove(OUT_PREFIX "c.bin");
remove(path);
free(bad);
}
int main(void)
{
static const struct member solid[3] = {
{ "a.bin", 1500, 0 }, { "b.bin", 900, 1 }, { "c.bin", 20000, 2 } };
static const struct member stored[2] = {
{ "a.bin", 1500, 0 }, { "b.bin", 900, 1 } };
static const struct member bcj2[1] = { { "prog.bin", 6000, 3 } };
/* big.bin, between the others in its folder, is streamed out where
* the folder is LZMA's or LZMA2's */
static const struct member big[3] = {
{ "a.bin", 1500, 0 }, { "big.bin", 700000, 2 }, { "c.bin", 900, 1 } };
check("archive_7z_test_solid.7z", fx_solid, sizeof(fx_solid), solid, 3);
check("archive_7z_test_stored.7z", fx_stored, sizeof(fx_stored), stored, 2);
check("archive_7z_test_bcj2.7z", fx_bcj2_copy, sizeof(fx_bcj2_copy), bcj2, 1);
check("archive_7z_test_lzma2.7z", fx_big_lzma2, sizeof(fx_big_lzma2), big, 3);
check("archive_7z_test_lzma.7z", fx_big_lzma, sizeof(fx_big_lzma), big, 3);
check("archive_7z_test_deflate.7z", fx_big_deflate, sizeof(fx_big_deflate),
big, 3);
/* three quarters into the packed stream, which runs from 32 to the
* header the start header points at: big.bin's share of it */
check_refused("archive_7z_test_bad.7z", fx_big_lzma2, sizeof(fx_big_lzma2),
32 + (fx_big_lzma2[12] | (fx_big_lzma2[13] << 8)) * 3 / 4);
if (failures)
{
printf("%u check(s) failed\n", failures);
return 1;
}
printf("archive_7z_test: every member read and extracted\n");
return 0;
}
+71
View File
@@ -0,0 +1,71 @@
include ../../sample_objdir.mk
TARGET := archive_bz2_test
LIBRETRO_COMM_DIR := ../../..
SOURCES := \
archive_bz2_test.c \
$(LIBRETRO_COMM_DIR)/compat/fopen_utf8.c \
$(LIBRETRO_COMM_DIR)/compat/compat_strl.c \
$(LIBRETRO_COMM_DIR)/compat/compat_strcasestr.c \
$(LIBRETRO_COMM_DIR)/compat/compat_posix_string.c \
$(LIBRETRO_COMM_DIR)/encodings/encoding_crc32.c \
$(LIBRETRO_COMM_DIR)/encodings/encoding_rbzip2.c \
$(LIBRETRO_COMM_DIR)/encodings/encoding_utf.c \
$(LIBRETRO_COMM_DIR)/encodings/encoding_deflate.c \
$(LIBRETRO_COMM_DIR)/features/features_cpu.c \
$(LIBRETRO_COMM_DIR)/file/archive_file.c \
$(LIBRETRO_COMM_DIR)/file/archive_file_zlib.c \
$(LIBRETRO_COMM_DIR)/file/archive_file_bz2.c \
$(LIBRETRO_COMM_DIR)/file/file_path.c \
$(LIBRETRO_COMM_DIR)/file/file_path_io.c \
$(LIBRETRO_COMM_DIR)/lists/string_list.c \
$(LIBRETRO_COMM_DIR)/streams/file_stream.c \
$(LIBRETRO_COMM_DIR)/streams/trans_stream.c \
$(LIBRETRO_COMM_DIR)/streams/trans_stream_pipe.c \
$(LIBRETRO_COMM_DIR)/streams/trans_stream_deflate.c \
$(LIBRETRO_COMM_DIR)/string/rstrtod.c \
$(LIBRETRO_COMM_DIR)/string/stdstring.c \
$(LIBRETRO_COMM_DIR)/vfs/vfs_implementation.c \
$(LIBRETRO_COMM_DIR)/time/rtime.c
OBJS := $(call sample_objs,$(SOURCES:.c=.o))
override CPPFLAGS += -DHAVE_COMPRESSION -DHAVE_RBZIP2 -I$(LIBRETRO_COMM_DIR)/include
CFLAGS += -Wall -pedantic -std=gnu99 -g1
# A .bz2 is kept from its listing up to 100000 octets, and one read
# through the stream wraps round a ring of 128 KiB. MMAP=1 maps it
# instead.
override CPPFLAGS += -DARCHIVE_BZ2_KEEP=100000 -DARCHIVE_BZ2_AHEAD=65536
MMAP ?= 0
ifeq ($(MMAP),1)
override CPPFLAGS += -DHAVE_MMAP
endif
ifneq ($(SANITIZER),)
CFLAGS := -fsanitize=$(SANITIZER) -fno-omit-frame-pointer $(CFLAGS)
LDFLAGS := -fsanitize=$(SANITIZER) $(LDFLAGS)
endif
all: $(TARGET)
%.o: %.c Makefile ../../encodings/rbzip2/bz_write.h
$(CC) -c -o $@ $< $(CPPFLAGS) $(CFLAGS)
$(SAMPLE_OBJDIR)/%.o: $$(call sample_src,$$*).c Makefile
@mkdir -p $(@D)
$(CC) -c -o $@ $< $(CPPFLAGS) $(CFLAGS)
$(TARGET): $(OBJS)
$(CC) -o $@ $^ $(LDFLAGS)
check: $(TARGET)
./$(TARGET)
clean:
rm -rf $(SAMPLE_OBJDIR)
rm -f $(TARGET) $(OBJS)
.PHONY: all check clean
+372
View File
@@ -0,0 +1,372 @@
/* Copyright (C) 2010-2026 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (archive_bz2_test.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* bzip2 in the archive layer: zip members stored with method 12, and
* .bz2 files, which stand for the file named as they are without the
* extension. Each is listed, read whole with
* file_archive_compressed_read() and extracted through a transfer, and
* must come out as it went in, zip members through an entry source as
* well; a zip member whose recorded CRC is wrong is refused, and a "#"
* after ".bz2" names a member. A .bz2 is kept from its listing when it
* is small and decoded again when it is not, through a ring where it is
* not mapped, and a changed one leaves nothing behind. The streams are
* bz_write.h's, as the rbzip2 sample makes them. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <boolean.h>
#include <file/archive_file.h>
#include <file/file_path.h>
#include <lists/string_list.h>
#include <encodings/crc32.h>
#include <streams/file_stream.h>
#include "../../encodings/rbzip2/bz_write.h"
#define ZIP_PATH "archive_bz2_test.zip"
#define BZ2_PATH "archive_bz2_test.bin.bz2"
#define OUT_PATH "archive_bz2_test.out"
#define N_MEMBERS 3
static unsigned failures;
static void fail(const char *what, const char *detail)
{
printf("[FAIL] %s: %s\n", what, detail);
failures++;
}
static void put16(uint8_t *p, unsigned v)
{
p[0] = (uint8_t)v;
p[1] = (uint8_t)(v >> 8);
}
static void put32(uint8_t *p, uint32_t v)
{
put16(p, v & 0xffff);
put16(p + 2, v >> 16);
}
static void fill(uint8_t *d, size_t n, unsigned k)
{
uint32_t x = 7 + k;
size_t i;
for (i = 0; i < n; i++)
{
x = x * 1103515245u + 12345u;
d[i] = (uint8_t)(k == 1 ? (x >> 16) : 'a' + (x >> 16) % (3 + k) + (i >> 10) % 3);
}
}
static bool write_file(const char *path, const uint8_t *d, size_t n)
{
FILE *f = fopen(path, "wb");
bool ok;
if (!f)
return false;
ok = fwrite(d, 1, n, f) == n;
fclose(f);
return ok;
}
/* the member's octets, as read back every way there is */
static void check_member(const char *what, const char *archive,
const char *name, const uint8_t *want, size_t n, uint32_t crc)
{
char path[256];
void *buf = NULL;
int64_t len = 0;
snprintf(path, sizeof(path), "%s#%s", archive, name);
if ( !file_archive_compressed_read(path, &buf, NULL, &len)
|| len != (int64_t)n || memcmp(buf, want, n))
fail(what, "read whole");
free(buf);
if (file_archive_get_file_crc32(path) != crc)
fail(what, "listed CRC");
}
static int extract_cb(const char *name, const char *valid_exts,
const uint8_t *cdata, unsigned cmode, uint64_t csize, uint64_t size,
uint32_t checksum, struct archive_extract_userdata *userdata)
{
char out[64];
snprintf(out, sizeof(out), "%s.%s", OUT_PATH, path_basename(name));
if (file_archive_perform_mode(out, valid_exts, cdata, cmode,
csize, size, checksum, userdata))
(*(unsigned*)userdata->cb_data)++;
return 1;
}
static unsigned extract_all(const char *archive)
{
struct archive_extract_userdata userdata;
file_archive_transfer_t state;
unsigned extracted = 0;
bool returnerr = true;
memset(&state, 0, sizeof(state));
memset(&userdata, 0, sizeof(userdata));
state.type = ARCHIVE_TRANSFER_INIT;
userdata.cb_data = &extracted;
while (file_archive_parse_file_iterate(&state, &returnerr, archive,
NULL, extract_cb, &userdata) == 0) { }
return extracted;
}
static void check_extracted(const char *what, const char *name,
const uint8_t *want, size_t n)
{
char out[64];
void *got = NULL;
int64_t got_len = 0;
if (name)
snprintf(out, sizeof(out), "%s.%s", OUT_PATH, name);
else
snprintf(out, sizeof(out), "%s", OUT_PATH);
if ( !filestream_read_file(out, &got, &got_len)
|| got_len != (int64_t)n || memcmp(got, want, n))
fail(what, "extracted");
free(got);
remove(out);
}
static void test_zip(bool wrong_crc)
{
static const size_t sizes[N_MEMBERS] = { 70000, 333, 260000 };
uint8_t *data[N_MEMBERS], *packed[N_MEMBERS], *zip;
size_t plen[N_MEMBERS], at = 0, cd, cap = 64;
uint32_t crc[N_MEMBERS], off[N_MEMBERS];
char name[16];
unsigned i;
for (i = 0; i < N_MEMBERS; i++)
{
size_t c = sizes[i] + sizes[i] / 2 + 4096;
data[i] = (uint8_t*)malloc(sizes[i]);
packed[i] = (uint8_t*)malloc(c);
fill(data[i], sizes[i], i);
crc[i] = encoding_crc32(0, data[i], sizes[i]) ^ (wrong_crc ? 1u : 0u);
plen[i] = bz_write(packed[i], c, data[i], sizes[i], 1 + i * 4, i);
cap += 30 + 46 + 2 * 16 + plen[i];
}
zip = (uint8_t*)malloc(cap + 22);
for (i = 0; i < N_MEMBERS; i++)
{
sprintf(name, "m%u.bin", i);
off[i] = (uint32_t)at;
memset(zip + at, 0, 30);
put32(zip + at, 0x04034b50u);
put16(zip + at + 4, 46);
put16(zip + at + 8, 12);
put32(zip + at + 14, crc[i]);
put32(zip + at + 18, (uint32_t)plen[i]);
put32(zip + at + 22, (uint32_t)sizes[i]);
put16(zip + at + 26, (unsigned)strlen(name));
memcpy(zip + at + 30, name, strlen(name));
at += 30 + strlen(name);
memcpy(zip + at, packed[i], plen[i]);
at += plen[i];
}
cd = at;
for (i = 0; i < N_MEMBERS; i++)
{
sprintf(name, "m%u.bin", i);
memset(zip + at, 0, 46);
put32(zip + at, 0x02014b50u);
put16(zip + at + 4, 46);
put16(zip + at + 6, 46);
put16(zip + at + 10, 12);
put32(zip + at + 16, crc[i]);
put32(zip + at + 20, (uint32_t)plen[i]);
put32(zip + at + 24, (uint32_t)sizes[i]);
put16(zip + at + 28, (unsigned)strlen(name));
put32(zip + at + 42, off[i]);
memcpy(zip + at + 46, name, strlen(name));
at += 46 + strlen(name);
}
memset(zip + at, 0, 22);
put32(zip + at, 0x06054b50u);
put16(zip + at + 8, N_MEMBERS);
put16(zip + at + 10, N_MEMBERS);
put32(zip + at + 12, (uint32_t)(at - cd));
put32(zip + at + 16, (uint32_t)cd);
at += 22;
if (!write_file(ZIP_PATH, zip, at))
fail("zip", "cannot write");
if (wrong_crc)
{
void *buf = NULL;
int64_t len = 0;
if (extract_all(ZIP_PATH) != 0)
fail("zip, CRCs wrong", "extracted");
if (file_archive_compressed_read(ZIP_PATH "#m1.bin", &buf, NULL, &len))
fail("zip, CRCs wrong", "read whole");
free(buf);
{
int64_t usize = 0;
uint8_t *got = (uint8_t*)malloc(sizes[0]);
file_archive_entry_source_t *src =
file_archive_entry_source_open(ZIP_PATH "#m0.bin", &usize);
if (!src || file_archive_entry_source_read(src, got, 4096) >= 0)
fail("zip, CRCs wrong", "read through an entry source");
if (src)
file_archive_entry_source_close(src);
free(got);
}
for (i = 0; i < N_MEMBERS; i++)
{
sprintf(name, "m%u.bin", i);
{
char out[64];
snprintf(out, sizeof(out), "%s.%s", OUT_PATH, name);
remove(out);
}
}
}
else
{
if (extract_all(ZIP_PATH) != N_MEMBERS)
fail("zip", "not every member extracted");
for (i = 0; i < N_MEMBERS; i++)
{
char member[64];
int64_t usize = 0, done = 0, got_n;
uint8_t *got = (uint8_t*)malloc(sizes[i] + 1);
file_archive_entry_source_t *src;
sprintf(name, "m%u.bin", i);
check_extracted("zip", name, data[i], sizes[i]);
check_member("zip", ZIP_PATH, name, data[i], sizes[i], crc[i]);
/* through an entry source, as content loads it */
snprintf(member, sizeof(member), "%s#%s", ZIP_PATH, name);
if ( !(src = file_archive_entry_source_open(member, &usize))
|| usize != (int64_t)sizes[i])
fail("zip", "no entry source");
else
{
while ((got_n = file_archive_entry_source_read(src, got + done,
4096)) > 0)
done += got_n;
if (done != (int64_t)sizes[i] || memcmp(got, data[i], sizes[i]))
fail("zip", "differs through an entry source");
}
if (src)
file_archive_entry_source_close(src);
free(got);
}
}
remove(ZIP_PATH);
for (i = 0; i < N_MEMBERS; i++)
{
free(data[i]);
free(packed[i]);
}
free(zip);
}
/* A .bz2 of @n octets: listed, read whole, read to a file and
* extracted, kept from its listing under ARCHIVE_BZ2_KEEP and decoded
* again past it; with a byte of it changed, nothing comes out */
static void test_bz2(const char *what, size_t n, unsigned level)
{
size_t c = n + n / 2 + 4096, plen;
uint8_t *data = (uint8_t*)malloc(n);
uint8_t *bz = (uint8_t*)malloc(c);
struct string_list *list;
void *buf = NULL;
int64_t len = 0;
fill(data, n, 2);
plen = bz_write(bz, c, data, n, level, 9);
if (!write_file(BZ2_PATH, bz, plen))
fail(what, "cannot write");
if ( !(list = file_archive_get_file_list(BZ2_PATH, NULL))
|| list->size != 1
|| strcmp(list->elems[0].data, "archive_bz2_test.bin"))
fail(what, "listed as its name without the extension");
string_list_free(list);
check_member(what, BZ2_PATH, "archive_bz2_test.bin", data, n,
encoding_crc32(0, data, n));
if (extract_all(BZ2_PATH) != 1)
fail(what, "not extracted");
check_extracted(what, "archive_bz2_test.bin", data, n);
if ( file_archive_compressed_read(BZ2_PATH "#archive_bz2_test.bin",
NULL, OUT_PATH, &len) <= 0
|| len != (int64_t)n)
fail(what, "read to a file");
check_extracted(what, NULL, data, n);
if (file_archive_compressed_read(BZ2_PATH "#other.bin", &buf, NULL, &len))
fail(what, "a member it does not have read");
free(buf);
buf = NULL;
/* a byte of the last block's changed */
bz[plen - 20] ^= 0x10;
if (!write_file(BZ2_PATH, bz, plen))
fail(what, "cannot write");
if (file_archive_compressed_read(BZ2_PATH "#archive_bz2_test.bin",
&buf, NULL, &len))
fail(what, "a changed stream read whole");
free(buf);
if (file_archive_compressed_read(BZ2_PATH "#archive_bz2_test.bin",
NULL, OUT_PATH, &len) || path_is_valid(OUT_PATH))
fail(what, "a changed stream read to a file");
remove(OUT_PATH);
extract_all(BZ2_PATH);
if (path_is_valid(OUT_PATH ".archive_bz2_test.bin"))
fail(what, "a changed stream extracted");
remove(OUT_PATH ".archive_bz2_test.bin");
remove(BZ2_PATH);
free(data);
free(bz);
}
int main(void)
{
const char *p = "roms/set.BZ2#game.bin";
if (path_get_archive_delim(p) != p + 12)
fail("path_get_archive_delim", "\".bz2#\" not a member");
if (!path_is_compressed_file("roms/set.bz2"))
fail("path_is_compressed_file", "set.bz2 is not an archive");
if (!path_is_compressed_file("roms/SET.Bz2"))
fail("path_is_compressed_file", "SET.Bz2 is not an archive");
if (path_is_compressed_file("roms/set.bz"))
fail("path_is_compressed_file", "set.bz is an archive");
if (!path_is_compressed_file("roms/set.7Z"))
fail("path_is_compressed_file", "set.7Z is not an archive");
test_zip(false);
test_zip(true);
/* kept from its listing, and too big to be */
test_bz2(".bz2", 60000, 2);
test_bz2(".bz2 past what is kept, through the ring", 1000000, 1);
if (failures)
{
printf("%u check(s) failed\n", failures);
return 1;
}
printf("archive_bz2_test: zip members and .bz2 files read and extracted\n");
return 0;
}
+6
View File
@@ -24,12 +24,18 @@ SOURCES := \
$(LIBRETRO_COMM_DIR)/streams/trans_stream_pipe.c \
$(LIBRETRO_COMM_DIR)/streams/trans_stream_deflate.c \
$(LIBRETRO_COMM_DIR)/encodings/encoding_deflate.c \
$(LIBRETRO_COMM_DIR)/encodings/encoding_rzstd.c \
$(LIBRETRO_COMM_DIR)/encodings/encoding_xxh64.c \
$(LIBRETRO_COMM_DIR)/vfs/vfs_implementation.c \
$(LIBRETRO_COMM_DIR)/time/rtime.c
OBJS := $(call sample_objs,$(SOURCES:.c=.o))
override CPPFLAGS += -DHAVE_COMPRESSION -I$(LIBRETRO_COMM_DIR)/include
# Zstandard members inside a zip, while .zst stays without a backend
archive_zip_test.o $(call sample_objs,$(LIBRETRO_COMM_DIR)/file/archive_file_zlib.o): \
override CPPFLAGS += -DHAVE_RZSTD
CFLAGS += -Wall -pedantic -std=gnu99 -g1
ifneq ($(SANITIZER),)
+314 -9
View File
@@ -59,6 +59,10 @@
* make SANITIZER=address
*/
/* the lending VFS installs the implementation's own functions as a
* frontend's */
#define VFS_FRONTEND
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -68,11 +72,65 @@
#include <lists/string_list.h>
#include <encodings/crc32.h>
#include <encodings/deflate.h>
#ifdef HAVE_RZSTD
#include <encodings/rzstd.h>
#endif
#include <streams/file_stream.h>
#include <file/file_path.h>
#include <vfs/vfs_implementation.h>
#include <libretro.h>
static int failures = 0;
/* A frontend VFS that lends the first part of the archive from its
* own copy and counts what it is asked to read */
static const uint8_t *lend_map;
static int64_t lend_len;
static uint64_t lend_read;
static const uint8_t *lend_mapped_ptr(void *hfile, int64_t *len)
{
(void)hfile;
*len = lend_len;
return lend_map;
}
static int64_t lend_read_cb(struct retro_vfs_file_handle *h, void *s,
uint64_t len)
{
int64_t r = retro_vfs_file_read_impl(h, s, len);
if (r > 0)
lend_read += (uint64_t)r;
return r;
}
static void lend_vfs(const uint8_t *map, int64_t len)
{
static struct retro_vfs_interface iface;
struct retro_vfs_interface_info info;
memset(&iface, 0, sizeof(iface));
iface.get_path = retro_vfs_file_get_path_impl;
iface.open = retro_vfs_file_open_impl;
iface.close = retro_vfs_file_close_impl;
iface.size = retro_vfs_file_size_impl;
iface.tell = retro_vfs_file_tell_impl;
iface.seek = retro_vfs_file_seek_impl;
iface.read = lend_read_cb;
iface.write = retro_vfs_file_write_impl;
iface.flush = retro_vfs_file_flush_impl;
iface.remove = retro_vfs_file_remove_impl;
iface.rename = retro_vfs_file_rename_impl;
lend_map = map;
lend_len = len;
lend_read = 0;
info.required_interface_version = map ? FILESTREAM_REQUIRED_VFS_VERSION : 0;
info.iface = map ? &iface : NULL;
filestream_vfs_init(&info);
filestream_set_mapped_ptr_cb(map ? lend_mapped_ptr : NULL);
}
/* --- tiny ZIP builders (little-endian byte layout) ----------------- */
static void put_u16(uint8_t *p, uint16_t v)
@@ -705,8 +763,16 @@ static void test_missing_backend_reports_failure(void)
* DEFLATE member spanning several 128 KiB input slices is followed by
* small DEFLATE and STORED ones, and every member must come out
* byte-exact. Run again with every recorded CRC wrong, every member
* must be refused and none written. */
#define SEQ_MEMBERS 6
* must be refused and none written. Run again through a VFS that
* lends the first members in place: they are inflated from what it
* lends, the large one through an entry source too, rather than read,
* and the rest are read. */
#define SEQ_MEMBERS 7
#ifdef HAVE_RZSTD
#define SEQ_ZSTD 93
#else
#define SEQ_ZSTD 0
#endif
typedef struct
{
@@ -750,8 +816,22 @@ static bool seq_deflate(seq_member_t *m)
return true;
}
#ifdef HAVE_RZSTD
static bool seq_zstd(seq_member_t *m)
{
size_t bound = rzstd_compress_bound(m->size);
size_t wrote = 0;
if ( !(m->packed = (uint8_t*)malloc(bound))
|| rzstd_encode(m->packed, bound, m->data, m->size, 3, &wrote)
!= RZSTD_PROCESS_END)
return false;
m->csize = (uint32_t)wrote;
return true;
}
#endif
static int seq_extract_cb(const char *name, const char *valid_exts,
const uint8_t *cdata, unsigned cmode, uint32_t csize, uint32_t size,
const uint8_t *cdata, unsigned cmode, uint64_t csize, uint64_t size,
uint32_t checksum, struct archive_extract_userdata *userdata)
{
char out[64];
@@ -764,11 +844,11 @@ static int seq_extract_cb(const char *name, const char *valid_exts,
return 1;
}
static void member_sequence(bool corrupt)
static void member_sequence(bool corrupt, bool lend)
{
static const unsigned methods[SEQ_MEMBERS] = { 8, 0, 8, 8, 0, 8 };
static const unsigned methods[SEQ_MEMBERS] = { 8, 0, 8, SEQ_ZSTD, 8, 0, 8 };
static const uint32_t sizes[SEQ_MEMBERS] =
{ 3000, 777, 512 * 1024, 5000, 1, 1 };
{ 3000, 777, 512 * 1024, 20000, 5000, 1, 1 };
const char *tmp_path = "rarch_zip_seq_test.zip";
seq_member_t m[SEQ_MEMBERS];
struct archive_extract_userdata userdata;
@@ -809,6 +889,13 @@ static void member_sequence(bool corrupt)
if (!seq_deflate(&m[i]))
ok = false;
}
#ifdef HAVE_RZSTD
else if (m[i].method == SEQ_ZSTD)
{
if (!seq_zstd(&m[i]))
ok = false;
}
#endif
else
m[i].csize = m[i].size;
cap += 30 + 46 + 2 * 16 + m[i].csize;
@@ -845,7 +932,7 @@ static void member_sequence(bool corrupt)
put_u16(buf + len + 28, 0);
memcpy(buf + len + 30, name, nl);
memcpy(buf + len + 30 + nl,
m[i].method == 8 ? m[i].packed : m[i].data, m[i].csize);
m[i].packed ? m[i].packed : m[i].data, m[i].csize);
len += 30 + nl + m[i].csize;
}
@@ -871,14 +958,165 @@ static void member_sequence(bool corrupt)
state.type = ARCHIVE_TRANSFER_INIT;
userdata.cb_data = &extracted;
/* lent up to the fifth member's header */
if (lend)
lend_vfs(buf, (int64_t)offsets[4]);
while (file_archive_parse_file_iterate(&state, &returnerr, tmp_path,
NULL, seq_extract_cb, &userdata) == 0) { }
if (lend)
{
uint64_t iterate_read = lend_read;
char member[64];
file_archive_entry_source_t *src;
int64_t usize = 0;
uint8_t *got = (uint8_t*)malloc(m[2].size);
int64_t done = 0;
/* the headers in the lent part are read, the members not */
if (iterate_read > len - offsets[4] + 4096)
{
printf("FAIL lent members: %llu octets read, not lent\n",
(unsigned long long)iterate_read);
ok = false;
}
snprintf(member, sizeof(member), "%s#m2.bin", tmp_path);
lend_read = 0;
if (!got || !(src = file_archive_entry_source_open(member, &usize))
|| usize != (int64_t)m[2].size)
ok = false;
else
{
for (;;)
{
int64_t n = file_archive_entry_source_read(src,
got + done, 4096);
if (n <= 0)
break;
done += n;
}
file_archive_entry_source_close(src);
if (done != (int64_t)m[2].size || memcmp(got, m[2].data, m[2].size))
{
printf("FAIL lent entry source: %lld of %u octets right\n",
(long long)done, m[2].size);
ok = false;
}
else if (lend_read >= m[2].csize)
{
printf("FAIL lent entry source: %llu octets read, not lent\n",
(unsigned long long)lend_read);
ok = false;
}
}
free(got);
lend_vfs(NULL, 0);
}
/* every member through an entry source, as content loads it; a
* member decoded whole is checked against its CRC there too */
if (!lend)
for (i = 0; i < SEQ_MEMBERS; i++)
{
char member[64];
int64_t usize = 0, done = 0, got_n;
uint8_t *got = (uint8_t*)malloc(m[i].size + 1);
file_archive_entry_source_t *src;
snprintf(member, sizeof(member), "%s#m%u.bin", tmp_path, i);
if (!(src = file_archive_entry_source_open(member, &usize))
|| usize != (int64_t)m[i].size)
{
printf("FAIL member sequence: m%u.bin (method %u) opens no "
"entry source\n", i, m[i].method);
ok = false;
}
else
{
while ((got_n = file_archive_entry_source_read(src,
got + done, 4096)) > 0)
done += got_n;
if (corrupt && m[i].method == SEQ_ZSTD && SEQ_ZSTD && got_n >= 0)
{
printf("FAIL member sequence: m%u.bin (method %u) read "
"through an entry source despite a wrong CRC\n",
i, m[i].method);
ok = false;
}
else if (!corrupt && (done != (int64_t)m[i].size
|| memcmp(got, m[i].data, m[i].size)))
{
printf("FAIL member sequence: m%u.bin (method %u) differs "
"through an entry source\n", i, m[i].method);
ok = false;
}
}
if (src)
file_archive_entry_source_close(src);
free(got);
}
/* read whole, a member failing its CRC is no member, not an
* empty one */
if (corrupt)
for (i = 0; i < SEQ_MEMBERS; i++)
{
char member[64];
void *got = NULL;
int64_t got_len = -1;
snprintf(member, sizeof(member), "%s#m%u.bin", tmp_path, i);
if (file_archive_compressed_read(member, &got, NULL, &got_len))
{
printf("FAIL member sequence: m%u.bin (method %u) read "
"whole despite a wrong CRC, %lld octets\n", i,
m[i].method, (long long)got_len);
ok = false;
}
free(got);
}
/* read to a file, as for a core that wants a path: written as it
* is decoded, a stored member from where it lies, and with a
* wrong CRC not written at all */
for (i = 0; i < SEQ_MEMBERS; i++)
{
char member[64];
void *got = NULL;
int64_t got_len = 0;
int64_t rd;
snprintf(member, sizeof(member), "%s#m%u.bin", tmp_path, i);
remove("rarch_zip_seq_file.bin");
rd = file_archive_compressed_read(member, NULL,
"rarch_zip_seq_file.bin", &got_len);
if (corrupt)
{
if (rd || path_is_valid("rarch_zip_seq_file.bin"))
{
printf("FAIL member sequence: m%u.bin (method %u) read to "
"a file despite a wrong CRC\n", i, m[i].method);
ok = false;
}
}
else if (!rd || !filestream_read_file("rarch_zip_seq_file.bin",
&got, &got_len) || got_len != (int64_t)m[i].size
|| memcmp(got, m[i].data, m[i].size))
{
printf("FAIL member sequence: m%u.bin (method %u) differs "
"read to a file\n", i, m[i].method);
ok = false;
}
free(got);
remove("rarch_zip_seq_file.bin");
}
remove(tmp_path);
if (extracted != (corrupt ? 0 : SEQ_MEMBERS))
ok = false;
for (i = 0; i < SEQ_MEMBERS; i++)
{
char out[64];
@@ -926,6 +1164,8 @@ static void member_sequence(bool corrupt)
}
else if (corrupt)
printf("ok members failing their recorded CRC are refused\n");
else if (lend)
printf("ok members are decoded from what the VFS lends\n");
else
printf("ok mixed DEFLATE/STORED members extract intact through "
"one transfer\n");
@@ -933,12 +1173,75 @@ static void member_sequence(bool corrupt)
static void test_member_sequence_reuse(void)
{
member_sequence(false);
member_sequence(false, false);
}
static void test_member_crc_mismatch(void)
{
member_sequence(true);
member_sequence(true, false);
}
static void test_member_lent(void)
{
member_sequence(false, true);
}
/* An entry source is opened only for a member that lies inside the
* archive: a central directory claiming more than the archive holds -
* a stored member longer than the file, a deflated one whose packed
* size runs past it, a header offset past it - is refused at open,
* not read from where the mapping ends. */
static void test_entry_source_bounds(void)
{
static const char payload[] = "short member";
const char *tmp_path = "rarch_zip_bounds_test.zip";
unsigned c;
for (c = 0; c < 4; c++)
{
uint8_t buf[512];
size_t len = 0, cd;
int64_t usize = 0;
char member[64];
file_archive_entry_source_t *src;
len += write_stored_lfh(buf, "m.bin", (const uint8_t*)payload,
sizeof(payload) - 1, encoding_crc32(0, (const uint8_t*)payload,
sizeof(payload) - 1));
cd = len;
len += write_stored_cfh(buf + len, "m.bin", sizeof(payload) - 1,
encoding_crc32(0, (const uint8_t*)payload, sizeof(payload) - 1), 0);
switch (c)
{
case 1: /* stored, and longer than the archive */
put_u32(buf + cd + 20, 1u << 20);
put_u32(buf + cd + 24, 1u << 20);
break;
case 2: /* deflated, packed past the end */
put_u16(buf + cd + 10, 8);
put_u32(buf + cd + 20, 1u << 20);
put_u32(buf + cd + 24, 64);
break;
case 3: /* its header past the end */
put_u32(buf + cd + 42, 1u << 20);
break;
default: /* as it is: opens */
break;
}
len += write_eocd(buf + len, 1, (uint32_t)(len - cd), (uint32_t)cd);
write_file(tmp_path, buf, len);
snprintf(member, sizeof(member), "%s#m.bin", tmp_path);
src = file_archive_entry_source_open(member, &usize);
if (c == 0 ? !src || usize != (int64_t)sizeof(payload) - 1 : src != NULL)
{
printf("[FAIL] entry source bounds, case %u: %s\n", c,
src ? "opened" : "refused");
failures++;
}
if (src)
file_archive_entry_source_close(src);
remove(tmp_path);
}
printf("[SUCCESS] entry sources open only for members inside the archive\n");
}
int main(void)
@@ -954,6 +1257,8 @@ int main(void)
test_missing_backend_reports_failure();
test_member_sequence_reuse();
test_member_crc_mismatch();
test_member_lent();
test_entry_source_bounds();
if (failures)
{
+80 -2
View File
@@ -24,8 +24,9 @@
* a .rar path is an archive (the file browser's Open Archive / Load
* Archive), "x.rar#member" names a member, the layer picks the RAR
* backend, lists the members it can unpack, reads one into memory or
* out to a file, and gives its CRC-32. The archives are the reader's
* own, in ../../formats/rrar/archives. */
* out to a file, and gives its CRC-32, computed where the archive has
* none. The archives are the reader's own, in
* ../../formats/rrar/archives. */
#include <stdio.h>
#include <stdlib.h>
@@ -159,6 +160,80 @@ static void check_extract(const char *dir)
filestream_delete(out);
}
static int extract_cb(const char *name, const char *valid_exts,
const uint8_t *cdata, unsigned cmode, uint64_t csize, uint64_t size,
uint32_t checksum, struct archive_extract_userdata *userdata)
{
if (file_archive_perform_mode((const char*)userdata->cb_data, valid_exts,
cdata, cmode, csize, size, checksum, userdata))
userdata->found_file = true;
return 1;
}
/* @archive's member, through a transfer, into @out */
static bool extract_to(const char *archive, const char *out)
{
struct archive_extract_userdata userdata;
file_archive_transfer_t state;
bool returnerr = true;
memset(&state, 0, sizeof(state));
memset(&userdata, 0, sizeof(userdata));
state.type = ARCHIVE_TRANSFER_INIT;
userdata.cb_data = (void*)out;
while (file_archive_parse_file_iterate(&state, &returnerr, archive,
NULL, extract_cb, &userdata) == 0) { }
return userdata.found_file;
}
/* A member bigger than its window and the room around it, extracted to
* a file: it goes there as it is unpacked, not whole through memory,
* and comes out right; with a byte of its data changed, no file is
* left. */
static void check_extract_streamed(const char *dir)
{
char path[2048];
const char *out = "archive_rar_test_big.out";
const char *bad = "archive_rar_test_bad.rar";
uint8_t *chunk = (uint8_t*)malloc(1 << 20);
void *arc = NULL;
int64_t len = 0, got = 0, rd;
uint32_t crc = 0;
RFILE *f;
archive_path(path, sizeof(path), dir, "ppmd_lzss_conversion.rar", NULL);
filestream_delete(out);
if (!extract_to(path, out))
fail("streamed extract", path);
if ((f = filestream_open(out, RETRO_VFS_FILE_ACCESS_READ,
RETRO_VFS_FILE_ACCESS_HINT_NONE)))
{
while ((rd = filestream_read(f, chunk, 1 << 20)) > 0)
{
crc = encoding_crc32(crc, chunk, (size_t)rd);
got += rd;
}
filestream_close(f);
}
if (got != 241647978 || crc != 0xf72b4477u)
fail("streamed extract", out);
filestream_delete(out);
if (filestream_read_file(path, &arc, &len) && len > 1000)
{
((uint8_t*)arc)[len / 2] ^= 0x10;
if (filestream_write_file(bad, arc, len))
{
extract_to(bad, out);
if (path_is_valid(out))
fail("streamed extract, a byte changed", "file left");
}
}
free(arc);
free(chunk);
filestream_delete(out);
filestream_delete(bad);
}
static void check_refused(const char *dir)
{
char path[2048];
@@ -199,7 +274,10 @@ int main(int argc, char *argv[])
check_read(dir, "rar5_compressed.rar", "test.bin", 1200, 0x7cca70cdu);
check_read(dir, "rar5_arm.rar", "elf-Linux-ARMv7-ls", 90808, 0x886f91ebu);
check_read(dir, "filter.rar", "bsdcat.exe", 204288, 0x4db10349u);
/* BLAKE2 in place of a CRC: the CRC is had by unpacking */
check_read(dir, "rar5_blake2.rar", "cebula.txt", 814, 0x7e5ec49eu);
check_extract(dir);
check_extract_streamed(dir);
check_refused(dir);
if (failures)
+69
View File
@@ -0,0 +1,69 @@
include ../../sample_objdir.mk
TARGET := archive_zip64_test
LIBRETRO_COMM_DIR := ../../..
# ZIP64 through the zip backend, mapped with MMAP=1 (the default) and
# read through the stream with MMAP=0. The archive past 4 GiB is a
# sparse file, removed afterwards; where the filesystem cannot hold it
# that part skips.
SOURCES := \
archive_zip64_test.c \
$(LIBRETRO_COMM_DIR)/compat/fopen_utf8.c \
$(LIBRETRO_COMM_DIR)/compat/compat_strl.c \
$(LIBRETRO_COMM_DIR)/compat/compat_strcasestr.c \
$(LIBRETRO_COMM_DIR)/compat/compat_posix_string.c \
$(LIBRETRO_COMM_DIR)/encodings/encoding_crc32.c \
$(LIBRETRO_COMM_DIR)/features/features_cpu.c \
$(LIBRETRO_COMM_DIR)/encodings/encoding_utf.c \
$(LIBRETRO_COMM_DIR)/file/archive_file.c \
$(LIBRETRO_COMM_DIR)/file/archive_file_zlib.c \
$(LIBRETRO_COMM_DIR)/file/file_path.c \
$(LIBRETRO_COMM_DIR)/file/file_path_io.c \
$(LIBRETRO_COMM_DIR)/lists/string_list.c \
$(LIBRETRO_COMM_DIR)/streams/file_stream.c \
$(LIBRETRO_COMM_DIR)/string/rstrtod.c \
$(LIBRETRO_COMM_DIR)/string/stdstring.c \
$(LIBRETRO_COMM_DIR)/streams/trans_stream.c \
$(LIBRETRO_COMM_DIR)/streams/trans_stream_pipe.c \
$(LIBRETRO_COMM_DIR)/streams/trans_stream_deflate.c \
$(LIBRETRO_COMM_DIR)/encodings/encoding_deflate.c \
$(LIBRETRO_COMM_DIR)/vfs/vfs_implementation.c \
$(LIBRETRO_COMM_DIR)/time/rtime.c
OBJS := $(call sample_objs,$(SOURCES:.c=.o))
override CPPFLAGS += -DHAVE_COMPRESSION -I$(LIBRETRO_COMM_DIR)/include
CFLAGS += -Wall -pedantic -std=gnu99 -g1
MMAP ?= 1
ifeq ($(MMAP),1)
override CPPFLAGS += -DHAVE_MMAP
endif
ifneq ($(SANITIZER),)
CFLAGS := -fsanitize=$(SANITIZER) -fno-omit-frame-pointer $(CFLAGS)
LDFLAGS := -fsanitize=$(SANITIZER) $(LDFLAGS)
endif
all: $(TARGET)
%.o: %.c Makefile
$(CC) -c -o $@ $< $(CPPFLAGS) $(CFLAGS)
$(SAMPLE_OBJDIR)/%.o: $$(call sample_src,$$*).c Makefile
@mkdir -p $(@D)
$(CC) -c -o $@ $< $(CPPFLAGS) $(CFLAGS)
$(TARGET): $(OBJS)
$(CC) -o $@ $^ $(LDFLAGS)
check: $(TARGET)
./$(TARGET)
clean:
rm -rf $(SAMPLE_OBJDIR)
rm -f $(TARGET) $(OBJS)
.PHONY: all check clean
@@ -0,0 +1,567 @@
/* Copyright (C) 2010-2026 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (archive_zip64_test.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* ZIP64 in the zip backend.
*
* A small archive written with every ZIP64 field it can carry: the end
* of the directory found through the ZIP64 record, members whose
* sizes and offset are in the ZIP64 extra field, beside one without.
* Each member is listed, read whole, extracted through a transfer (a
* stored member bigger than the extraction window going through it a
* window at a time) and read through an entry source. An extra field
* short of what it must hold, a locator that is not one and a record
* past the end are refused.
*
* Then a real one, sparse: a stored member of 4 GiB and 4 KiB, and a
* member after it whose local header is past 4 GiB, with the
* directory after that. The big member's 64-bit size is listed, and
* the member after it is read every way. A filesystem that will not
* hold the file skips that part.
*
* Build: make (SANITIZER=address,undefined for a checked run)
* Run: ./archive_zip64_test
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <boolean.h>
#include <file/archive_file.h>
#include <file/file_path.h>
#include <lists/string_list.h>
#include <encodings/crc32.h>
#include <encodings/deflate.h>
#include <streams/file_stream.h>
#if defined(_WIN32)
#include <windows.h>
#include <winioctl.h>
#else
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#endif
#define ZIP_PATH "archive_zip64_test.zip"
#define BIG_PATH "archive_zip64_test_big.zip"
#define OUT_PATH "archive_zip64_test.out"
#define STORED_LEN 300000
#define DEFLATE_LEN 70000
#define BIG_LEN (((uint64_t)1 << 32) + 4096)
static unsigned failures;
static void fail(const char *what, const char *detail)
{
printf("[FAIL] %s: %s\n", what, detail);
failures++;
}
static void put16(uint8_t *p, unsigned v)
{
p[0] = (uint8_t)v;
p[1] = (uint8_t)(v >> 8);
}
static void put32(uint8_t *p, uint32_t v)
{
put16(p, v & 0xffff);
put16(p + 2, v >> 16);
}
static void put64(uint8_t *p, uint64_t v)
{
put32(p, (uint32_t)v);
put32(p + 4, (uint32_t)(v >> 32));
}
static void fill(uint8_t *d, size_t n, uint32_t x, unsigned alpha)
{
size_t i;
for (i = 0; i < n; i++)
{
x = x * 1103515245u + 12345u;
d[i] = (uint8_t)('a' + (x >> 16) % alpha);
}
}
/* raw DEFLATE of @in into @out; its length */
static size_t deflate_raw(uint8_t *out, size_t cap, const uint8_t *in,
size_t n)
{
void *z = rdeflate_new(6, -15);
size_t rd = 0, wr = 0, total = 0;
int st;
rdeflate_set_in(z, in, n);
rdeflate_finish(z);
do
{
rdeflate_set_out(z, out + total, cap - total);
st = rdeflate_process(z, &rd, &wr);
total += wr;
} while (st == RDEFLATE_PROCESS_NEXT && wr);
rdeflate_free(z);
return st == RDEFLATE_PROCESS_END ? total : 0;
}
/* One member of an archive being written: what the headers say */
struct member
{
const char *name;
const uint8_t *data;
uint64_t usize;
uint64_t csize;
uint64_t offset;
uint32_t crc;
unsigned method;
bool big_sizes; /* sizes in the ZIP64 field */
bool big_offset; /* the offset in it too */
};
/* The ZIP64 extra field for @m at @p; its length */
static size_t zip64_field(uint8_t *p, const struct member *m, bool local)
{
size_t n = 0;
if (m->big_sizes || local)
{
put64(p + 4 + n, m->usize);
put64(p + 4 + n + 8, m->csize);
n += 16;
}
if (m->big_offset && !local)
{
put64(p + 4 + n, m->offset);
n += 8;
}
if (!n)
return 0;
put16(p, 0x0001);
put16(p + 2, (unsigned)n);
return 4 + n;
}
static size_t local_header(uint8_t *p, const struct member *m)
{
size_t nl = strlen(m->name), el;
memset(p, 0, 30);
put32(p, 0x04034b50u);
put16(p + 4, 45);
put16(p + 8, m->method);
put32(p + 14, m->crc);
el = m->big_sizes ? zip64_field(p + 30 + nl, m, true) : 0;
put32(p + 18, m->big_sizes ? 0xFFFFFFFFu : (uint32_t)m->csize);
put32(p + 22, m->big_sizes ? 0xFFFFFFFFu : (uint32_t)m->usize);
put16(p + 26, (unsigned)nl);
put16(p + 28, (unsigned)el);
memcpy(p + 30, m->name, nl);
return 30 + nl + el;
}
static size_t central_header(uint8_t *p, const struct member *m)
{
size_t nl = strlen(m->name);
size_t el = zip64_field(p + 46 + nl, m, false);
memset(p, 0, 46);
put32(p, 0x02014b50u);
put16(p + 4, 45);
put16(p + 6, 45);
put16(p + 10, m->method);
put32(p + 16, m->crc);
put32(p + 20, m->big_sizes ? 0xFFFFFFFFu : (uint32_t)m->csize);
put32(p + 24, m->big_sizes ? 0xFFFFFFFFu : (uint32_t)m->usize);
put16(p + 28, (unsigned)nl);
put16(p + 30, (unsigned)el);
put32(p + 42, m->big_offset ? 0xFFFFFFFFu : (uint32_t)m->offset);
memcpy(p + 46, m->name, nl);
return 46 + nl + el;
}
/* The ZIP64 end record, its locator and the end record, the directory
* of @entries at @cd, @cd_len long, the record at @at */
static size_t directory_end(uint8_t *p, uint64_t at, uint64_t cd,
uint64_t cd_len, unsigned entries)
{
memset(p, 0, 56 + 20 + 22);
put32(p, 0x06064b50u);
put64(p + 4, 44);
put16(p + 12, 45);
put16(p + 14, 45);
put64(p + 24, entries);
put64(p + 32, entries);
put64(p + 40, cd_len);
put64(p + 48, cd);
put32(p + 56, 0x07064b50u);
put64(p + 56 + 8, at);
put32(p + 56 + 16, 1);
put32(p + 76, 0x06054b50u);
put16(p + 76 + 8, 0xFFFF);
put16(p + 76 + 10, 0xFFFF);
put32(p + 76 + 12, 0xFFFFFFFFu);
put32(p + 76 + 16, 0xFFFFFFFFu);
return 56 + 20 + 22;
}
static bool write_file(const char *path, const uint8_t *d, size_t n)
{
FILE *f = fopen(path, "wb");
bool ok;
if (!f)
return false;
ok = fwrite(d, 1, n, f) == n;
fclose(f);
return ok;
}
/* every member named @only, or all of them when NULL */
struct extract_want
{
const char *only;
unsigned count;
};
static int extract_one_cb(const char *name, const char *valid_exts,
const uint8_t *cdata, unsigned cmode, uint64_t csize, uint64_t size,
uint32_t checksum, struct archive_extract_userdata *userdata)
{
struct extract_want *w = (struct extract_want*)userdata->cb_data;
char out[96];
if (w->only && strcmp(name, w->only))
return 1;
snprintf(out, sizeof(out), "%s.%s", OUT_PATH, path_basename(name));
if (file_archive_perform_mode(out, valid_exts, cdata, cmode,
csize, size, checksum, userdata))
w->count++;
return 1;
}
static unsigned extract(const char *archive, const char *only)
{
struct archive_extract_userdata userdata;
struct extract_want want;
file_archive_transfer_t state;
bool returnerr = true;
memset(&state, 0, sizeof(state));
memset(&userdata, 0, sizeof(userdata));
want.only = only;
want.count = 0;
state.type = ARCHIVE_TRANSFER_INIT;
userdata.cb_data = &want;
while (file_archive_parse_file_iterate(&state, &returnerr, archive,
NULL, extract_one_cb, &userdata) == 0) { }
return want.count;
}
/* the member's octets, read every way there is */
static void check_member(const char *what, const char *archive,
const char *name, const uint8_t *want, size_t n)
{
char path[160], out[96];
void *buf = NULL;
int64_t len = 0, usize = 0, done = 0, got_n;
uint64_t listed = 0;
uint8_t *got = (uint8_t*)malloc(n + 1);
file_archive_entry_source_t *src;
snprintf(path, sizeof(path), "%s#%s", archive, name);
if ( !file_archive_compressed_read(path, &buf, NULL, &len)
|| len != (int64_t)n || memcmp(buf, want, n))
fail(what, "read whole");
free(buf);
if ( file_archive_get_file_crc32_and_size(path, &listed)
!= encoding_crc32(0, want, n)
|| listed != n)
fail(what, "listed CRC and size");
if (extract(archive, name) != 1)
fail(what, "not extracted");
snprintf(out, sizeof(out), "%s.%s", OUT_PATH, name);
buf = NULL;
if ( !filestream_read_file(out, &buf, &len)
|| len != (int64_t)n || memcmp(buf, want, n))
fail(what, "extracted");
free(buf);
remove(out);
if ( !(src = file_archive_entry_source_open(path, &usize))
|| usize != (int64_t)n)
fail(what, "no entry source");
else
{
while ((got_n = file_archive_entry_source_read(src, got + done,
4096)) > 0)
done += got_n;
if (done != (int64_t)n || memcmp(got, want, n))
fail(what, "differs through an entry source");
}
if (src)
file_archive_entry_source_close(src);
free(got);
}
static bool listed(const char *archive, unsigned n)
{
struct string_list *list = file_archive_get_file_list(archive, NULL);
bool ok = list && list->size == n;
string_list_free(list);
return ok;
}
static void test_small(void)
{
uint8_t *stored = (uint8_t*)malloc(STORED_LEN);
uint8_t *plain = (uint8_t*)malloc(DEFLATE_LEN);
uint8_t *packed = (uint8_t*)malloc(DEFLATE_LEN + 1024);
uint8_t *zip = (uint8_t*)malloc(STORED_LEN + DEFLATE_LEN + 4096);
uint8_t *bad = (uint8_t*)malloc(STORED_LEN + DEFLATE_LEN + 4096);
static const uint8_t note[] = "a member with nothing of ZIP64 about it\n";
struct member m[3];
size_t at = 0, cd, cd_len, plen, n, ext1 = 0, rec;
unsigned i;
fill(stored, STORED_LEN, 1, 7);
fill(plain, DEFLATE_LEN, 2, 5);
plen = deflate_raw(packed, DEFLATE_LEN + 1024, plain, DEFLATE_LEN);
if (!plen)
fail("ZIP64", "cannot deflate");
m[0].name = "stored.bin"; m[0].data = stored; m[0].method = 0;
m[0].usize = m[0].csize = STORED_LEN;
m[0].big_sizes = true; m[0].big_offset = false;
m[1].name = "deflate.bin"; m[1].data = packed; m[1].method = 8;
m[1].usize = DEFLATE_LEN; m[1].csize = plen;
m[1].big_sizes = true; m[1].big_offset = true;
m[2].name = "plain.txt"; m[2].data = note; m[2].method = 0;
m[2].usize = m[2].csize = sizeof(note) - 1;
m[2].big_sizes = false; m[2].big_offset = false;
m[0].crc = encoding_crc32(0, stored, STORED_LEN);
m[1].crc = encoding_crc32(0, plain, DEFLATE_LEN);
m[2].crc = encoding_crc32(0, note, sizeof(note) - 1);
for (i = 0; i < 3; i++)
{
m[i].offset = at;
at += local_header(zip + at, &m[i]);
memcpy(zip + at, m[i].data, (size_t)m[i].csize);
at += (size_t)m[i].csize;
}
cd = at;
for (i = 0; i < 3; i++)
{
if (i == 1)
ext1 = at + 46 + strlen(m[1].name);
at += central_header(zip + at, &m[i]);
}
cd_len = at - cd;
rec = at;
at += directory_end(zip + at, rec, cd, cd_len, 3);
n = at;
if (!write_file(ZIP_PATH, zip, n))
fail("ZIP64", "cannot write");
if (!listed(ZIP_PATH, 3))
fail("ZIP64", "listed");
check_member("ZIP64, stored", ZIP_PATH, "stored.bin", stored, STORED_LEN);
check_member("ZIP64, deflated", ZIP_PATH, "deflate.bin", plain,
DEFLATE_LEN);
check_member("ZIP64, neither", ZIP_PATH, "plain.txt", note,
sizeof(note) - 1);
/* deflate.bin's field holds two of the three values it must */
memcpy(bad, zip, n);
put16(bad + ext1 + 2, 16);
if (write_file(ZIP_PATH, bad, n))
{
void *buf = NULL;
int64_t len = 0;
if (file_archive_compressed_read(ZIP_PATH "#deflate.bin", &buf, NULL,
&len))
fail("ZIP64, extra field short", "read");
free(buf);
}
/* the locator, not one */
memcpy(bad, zip, n);
bad[rec + 56] ^= 0xFF;
if (write_file(ZIP_PATH, bad, n) && listed(ZIP_PATH, 3))
fail("ZIP64, no locator", "listed");
/* the record it points at, past the end */
memcpy(bad, zip, n);
put64(bad + rec + 56 + 8, (uint64_t)n + 100);
if (write_file(ZIP_PATH, bad, n) && listed(ZIP_PATH, 3))
fail("ZIP64, record past the end", "listed");
remove(ZIP_PATH);
free(stored);
free(plain);
free(packed);
free(zip);
free(bad);
}
/* A sparse file of @size with @n spans of bytes written into it, made
* without the code under test. false where it cannot be had. */
struct span
{
uint64_t at;
const uint8_t *data;
size_t len;
};
static bool make_sparse(const char *path, uint64_t size,
const struct span *spans, unsigned n)
{
unsigned i;
#if defined(_WIN32)
LARGE_INTEGER pos;
DWORD written, junk;
HANDLE h = CreateFileA(path, GENERIC_READ | GENERIC_WRITE, 0,
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (h == INVALID_HANDLE_VALUE)
return false;
DeviceIoControl(h, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, &junk, NULL);
pos.QuadPart = (LONGLONG)size;
if (!SetFilePointerEx(h, pos, NULL, FILE_BEGIN) || !SetEndOfFile(h))
{
CloseHandle(h);
DeleteFileA(path);
return false;
}
for (i = 0; i < n; i++)
{
pos.QuadPart = (LONGLONG)spans[i].at;
if ( !SetFilePointerEx(h, pos, NULL, FILE_BEGIN)
|| !WriteFile(h, spans[i].data, (DWORD)spans[i].len, &written,
NULL)
|| written != spans[i].len)
{
CloseHandle(h);
DeleteFileA(path);
return false;
}
}
CloseHandle(h);
return true;
#else
int fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0644);
if (fd < 0)
return false;
if (sizeof(off_t) < sizeof(uint64_t) || ftruncate(fd, (off_t)size) != 0)
{
close(fd);
remove(path);
return false;
}
for (i = 0; i < n; i++)
if ( lseek(fd, (off_t)spans[i].at, SEEK_SET) != (off_t)spans[i].at
|| write(fd, spans[i].data, spans[i].len)
!= (ssize_t)spans[i].len)
{
close(fd);
remove(path);
return false;
}
close(fd);
return true;
#endif
}
static void test_big(void)
{
static const uint8_t after[] = "the member after four gibibytes\n";
uint8_t head[128], tail[512];
struct member m[2];
struct span sp[2];
uint64_t at, cd, size;
size_t n0, t = 0;
unsigned i;
m[0].name = "big.iso"; m[0].data = NULL; m[0].method = 0;
m[0].usize = m[0].csize = BIG_LEN;
m[0].crc = 0x12345678u;
m[0].big_sizes = true; m[0].big_offset = false;
m[0].offset = 0;
m[1].name = "after.txt"; m[1].data = after; m[1].method = 0;
m[1].usize = m[1].csize = sizeof(after) - 1;
m[1].crc = encoding_crc32(0, after, sizeof(after) - 1);
m[1].big_sizes = false; m[1].big_offset = true;
n0 = local_header(head, &m[0]);
m[1].offset = n0 + BIG_LEN;
t += local_header(tail, &m[1]);
memcpy(tail + t, after, sizeof(after) - 1);
t += sizeof(after) - 1;
cd = m[1].offset + t;
at = 0;
for (i = 0; i < 2; i++)
at += central_header(tail + t + (size_t)at, &m[i]);
t += directory_end(tail + t + (size_t)at, cd + at, cd, at, 2);
t += (size_t)at;
size = m[1].offset + t;
sp[0].at = 0; sp[0].data = head; sp[0].len = n0;
sp[1].at = m[1].offset; sp[1].data = tail; sp[1].len = t;
if (!make_sparse(BIG_PATH, size, sp, 2))
{
printf("skip: no room for a sparse archive of %llu octets\n",
(unsigned long long)size);
return;
}
if (!listed(BIG_PATH, 2))
fail("past 4 GiB", "listed");
{
uint64_t len = 0;
if ( file_archive_get_file_crc32_and_size(BIG_PATH "#big.iso", &len)
!= 0x12345678u
|| len != BIG_LEN)
fail("past 4 GiB", "the big member's size");
}
{
int64_t usize = 0;
file_archive_entry_source_t *src =
file_archive_entry_source_open(BIG_PATH "#big.iso", &usize);
if (!src || usize != (int64_t)BIG_LEN)
fail("past 4 GiB", "the big member's entry source");
if (src)
file_archive_entry_source_close(src);
}
check_member("past 4 GiB, the member after", BIG_PATH, "after.txt",
after, sizeof(after) - 1);
remove(BIG_PATH);
}
int main(void)
{
test_small();
test_big();
if (failures)
{
printf("%u check(s) failed\n", failures);
return 1;
}
printf("archive_zip64_test: ZIP64 members listed, read and extracted\n");
return 0;
}
+85
View File
@@ -0,0 +1,85 @@
include ../../sample_objdir.mk
TARGET := archive_zip_methods_test
LIBRETRO_COMM_DIR := ../../..
# Deflate64, LZMA, bzip2 and Zstandard zip members, mapped with MMAP=1 (the
# default) and read through the stream with MMAP=0. HAVE_7ZIP brings
# the LZMA decoder.
SOURCES := \
archive_zip_methods_test.c \
$(LIBRETRO_COMM_DIR)/compat/fopen_utf8.c \
$(LIBRETRO_COMM_DIR)/compat/compat_strl.c \
$(LIBRETRO_COMM_DIR)/compat/compat_strcasestr.c \
$(LIBRETRO_COMM_DIR)/compat/compat_posix_string.c \
$(LIBRETRO_COMM_DIR)/encodings/encoding_crc32.c \
$(LIBRETRO_COMM_DIR)/features/features_cpu.c \
$(LIBRETRO_COMM_DIR)/encodings/encoding_utf.c \
$(LIBRETRO_COMM_DIR)/file/archive_file.c \
$(LIBRETRO_COMM_DIR)/file/archive_file_zlib.c \
$(LIBRETRO_COMM_DIR)/file/archive_file_7z.c \
$(LIBRETRO_COMM_DIR)/file/archive_file_bz2.c \
$(LIBRETRO_COMM_DIR)/file/archive_file_zstd.c \
$(LIBRETRO_COMM_DIR)/formats/7z/r7z_archive.c \
$(LIBRETRO_COMM_DIR)/formats/7z/r7z_lzma.c \
$(LIBRETRO_COMM_DIR)/formats/7z/r7z_lzma_stream.c \
$(LIBRETRO_COMM_DIR)/formats/7z/r7z_lzma2.c \
$(LIBRETRO_COMM_DIR)/formats/7z/r7z_bcj2.c \
$(LIBRETRO_COMM_DIR)/formats/7z/r7z_filters.c \
$(LIBRETRO_COMM_DIR)/formats/rar/rrar_ppmd7.c \
$(LIBRETRO_COMM_DIR)/file/file_path.c \
$(LIBRETRO_COMM_DIR)/file/file_path_io.c \
$(LIBRETRO_COMM_DIR)/lists/string_list.c \
$(LIBRETRO_COMM_DIR)/streams/file_stream.c \
$(LIBRETRO_COMM_DIR)/string/rstrtod.c \
$(LIBRETRO_COMM_DIR)/string/stdstring.c \
$(LIBRETRO_COMM_DIR)/streams/trans_stream.c \
$(LIBRETRO_COMM_DIR)/streams/trans_stream_pipe.c \
$(LIBRETRO_COMM_DIR)/streams/trans_stream_deflate.c \
$(LIBRETRO_COMM_DIR)/encodings/encoding_deflate.c \
$(LIBRETRO_COMM_DIR)/encodings/encoding_rbzip2.c \
$(LIBRETRO_COMM_DIR)/encodings/encoding_rzstd.c \
$(LIBRETRO_COMM_DIR)/encodings/encoding_xxh64.c \
$(LIBRETRO_COMM_DIR)/vfs/vfs_implementation.c \
$(LIBRETRO_COMM_DIR)/time/rtime.c
OBJS := $(call sample_objs,$(SOURCES:.c=.o))
override CPPFLAGS += -DHAVE_COMPRESSION -DHAVE_7ZIP -DHAVE_RBZIP2 -DHAVE_RZSTD -I$(LIBRETRO_COMM_DIR)/include
CFLAGS += -Wall -pedantic -std=gnu99 -g1
# A bzip2 member read with MMAP=0 goes through a ring that wraps round
# within it
override CPPFLAGS += -DZIP_BZIP2_AHEAD=1500
MMAP ?= 1
ifeq ($(MMAP),1)
override CPPFLAGS += -DHAVE_MMAP
endif
ifneq ($(SANITIZER),)
CFLAGS := -fsanitize=$(SANITIZER) -fno-omit-frame-pointer $(CFLAGS)
LDFLAGS := -fsanitize=$(SANITIZER) $(LDFLAGS)
endif
all: $(TARGET)
%.o: %.c Makefile archive_zip_methods_fixtures.h
$(CC) -c -o $@ $< $(CPPFLAGS) $(CFLAGS)
$(SAMPLE_OBJDIR)/%.o: $$(call sample_src,$$*).c Makefile
@mkdir -p $(@D)
$(CC) -c -o $@ $< $(CPPFLAGS) $(CFLAGS)
$(TARGET): $(OBJS)
$(CC) -o $@ $^ $(LDFLAGS)
check: $(TARGET)
./$(TARGET)
clean:
rm -rf $(SAMPLE_OBJDIR)
rm -f $(TARGET) $(OBJS)
.PHONY: all check clean
Loaded 100 of 171 files, more files were not shown because too many files have changed in this diff. Show more