Skip to content

Commit 4f5aeea

Browse files
committed
Make strong_number construct only from non-narrowing input
1 parent e8b5db0 commit 4f5aeea

9 files changed

Lines changed: 92 additions & 49 deletions

File tree

alc/alu.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2329,9 +2329,9 @@ template<> [[nodiscard]] auto SampleConv(f32 const val) noexcept -> i32
23292329
return fastf2i(std::clamp(val*2147483648.0f, -2147483648.0f, 2147483520.0f));
23302330
}
23312331
template<> [[nodiscard]] auto SampleConv(f32 const val) noexcept -> i16
2332-
{ return i16{gsl::narrow_cast<i16>(fastf2i(std::clamp(val*32768.0f, -32768.0f, 32767.0f)))}; }
2332+
{ return i16{gsl::narrow_cast<i16::value_t>(fastf2i(std::clamp(val*32768.0f, -32768.0f, 32767.0f)))}; }
23332333
template<> [[nodiscard]] auto SampleConv(f32 const val) noexcept -> i8
2334-
{ return i8{gsl::narrow_cast<std::int8_t>(fastf2i(std::clamp(val*128.0f, -128.0f, 127.0f)))}; }
2334+
{ return i8{gsl::narrow_cast<i8::value_t>(fastf2i(std::clamp(val*128.0f, -128.0f, 127.0f)))}; }
23352335

23362336
/* Define unsigned output variations. */
23372337
template<> [[nodiscard]] auto SampleConv(f32 const val) noexcept -> u32

alc/backends/wave.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -334,19 +334,19 @@ auto WaveBackend::reset() -> bool
334334
// 16-bit val, format type id (extensible: 0xFFFE)
335335
fwrite16le(0xFFFE_u16, mFile);
336336
// 16-bit val, channel count
337-
fwrite16le(u16{channels}, mFile);
337+
fwrite16le(u16::make_from(channels), mFile);
338338
// 32-bit val, frequency
339339
fwrite32le(mDevice->mSampleRate, mFile);
340340
// 32-bit val, bytes per second
341341
fwrite32le(mDevice->mSampleRate * channels * bytes, mFile);
342342
// 16-bit val, frame size
343-
fwrite16le(u16{channels * bytes}, mFile);
343+
fwrite16le(u16::make_from(channels * bytes), mFile);
344344
// 16-bit val, bits per sample
345-
fwrite16le(u16{bytes * 8}, mFile);
345+
fwrite16le(u16::make_from(bytes * 8), mFile);
346346
// 16-bit val, extra byte count
347347
fwrite16le(22_u16, mFile);
348348
// 16-bit val, valid bits per sample
349-
fwrite16le(u16{bytes * 8}, mFile);
349+
fwrite16le(u16::make_from(bytes * 8), mFile);
350350
// 32-bit val, channel mask
351351
fwrite32le(chanmask, mFile);
352352
// 16 byte GUID, sub-type format

common/alcomplex.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ struct BitReverser {
5252

5353
if(idx < revidx)
5454
{
55-
mData[ret_i][0] = u16{idx};
56-
mData[ret_i][1] = u16{revidx};
55+
mData[ret_i][0] = u16::make_from(idx);
56+
mData[ret_i][1] = u16::make_from(revidx);
5757
++ret_i;
5858
}
5959
}

common/altypes.hpp

Lines changed: 62 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
#include "gsl/gsl"
1414

1515

16+
struct UInt;
17+
1618
namespace al {
1719

1820
/* A "weak number" is a standard number type. They are prone to implicit
@@ -131,14 +133,41 @@ class number_base {
131133
using value_t = T;
132134
T c_val;
133135

134-
template<weak_number U> force_inline constexpr explicit
135-
number_base(U const &value) noexcept(not can_narrow<T, U>) : c_val{convert_to<T>(value)} { }
136+
template<weak_number U> requires(not can_narrow<T, U>) force_inline constexpr explicit
137+
number_base(U const &value) noexcept : c_val{convert_to<T>(value)} { }
138+
139+
force_inline constexpr explicit
140+
number_base(ConstantNum<T> const &value) noexcept : c_val{value.c_val} { }
141+
142+
template<weak_number U> force_inline static constexpr
143+
auto make_from(U const &value) noexcept(not can_narrow<T, U>) -> SelfType
144+
{
145+
/* Converting to a floating point type isn't checked here because it's
146+
* nearly impossible to otherwise ensure a large enough integer or
147+
* precise enough double will correctly fit in the target type. Since
148+
* converting to floating point results in the nearest value instead of
149+
* being modulo wrapped, this should be considered fine.
150+
*/
151+
if constexpr(std::floating_point<T>)
152+
return SelfType{static_cast<T>(value)};
153+
else
154+
return SelfType{convert_to<T>(value)};
155+
}
156+
157+
[[nodiscard]] static constexpr
158+
auto bit_pack(std::byte const hi, std::byte const lo) noexcept -> SelfType
159+
requires(sizeof(SelfType) == 2)
160+
{
161+
auto const ret = static_cast<std::uint16_t>((to_integer<std::uint16_t>(hi)<<8)
162+
| to_integer<std::uint16_t>(lo));
163+
return std::bit_cast<SelfType>(ret);
164+
}
136165

137166
/* Copy assignment from another strong number type, only for types that
138167
* won't narrow.
139168
*/
140-
template<strong_number U> requires(not std::is_base_of_v<number_base, U>)
141-
constexpr auto operator=(U const &rhs) & noexcept LIFETIMEBOUND -> number_base&
169+
template<strong_number U> requires(not std::is_base_of_v<number_base, U>) constexpr
170+
auto operator=(U const &rhs) & noexcept LIFETIMEBOUND -> number_base&
142171
{
143172
static_assert(not can_narrow<T, typename U::value_t>,
144173
"Invalid narrowing assignment; use .cast_to<U>() or .reinterpret_as<U>() to convert");
@@ -172,11 +201,8 @@ class number_base {
172201
noexcept(not can_narrow<typename U::value_t,T> or std::floating_point<typename U::value_t>)
173202
-> U
174203
{
175-
/* Converting to a floating point type isn't checked here because it's
176-
* nearly impossible to otherwise ensure a large enough integer or
177-
* precise enough double will correctly fit in the target type. Since
178-
* converting to floating point results in the nearest value instead of
179-
* being modulo wrapped, this should be considered fine.
204+
/* Like make_from, converting to a floating point type isn't checked
205+
* here.
180206
*/
181207
if constexpr(std::floating_point<typename U::value_t>)
182208
return U{static_cast<U::value_t>(c_val)};
@@ -190,6 +216,8 @@ class number_base {
190216
template<strong_number U> [[nodiscard]] constexpr
191217
auto reinterpret_as() const noexcept -> U { return U{static_cast<U::value_t>(c_val)}; }
192218

219+
[[nodiscard]] constexpr auto popcount() const noexcept -> UInt requires(std::integral<T>);
220+
193221
/* Relevant values for the given type. Offered here as static methods
194222
* instead of through a separate templated structure.
195223
*/
@@ -206,15 +234,6 @@ class number_base {
206234
requires std::numeric_limits<T>::has_signaling_NaN
207235
{ return SelfType{std::numeric_limits<T>::signaling_NaN()}; }
208236

209-
[[nodiscard]] static constexpr
210-
auto bit_pack(std::byte const hi, std::byte const lo) noexcept -> SelfType requires (sizeof(SelfType) == 2)
211-
{
212-
using unsigned_t = std::make_unsigned_t<T>;
213-
auto ret = static_cast<unsigned_t>((to_integer<unsigned_t>(hi)<<8)
214-
| to_integer<unsigned_t>(lo));
215-
return std::bit_cast<SelfType>(ret);
216-
}
217-
218237
/* Prefix and postfix increment and decrement operators. Only valid for
219238
* integral types.
220239
*/
@@ -314,13 +333,15 @@ class number_base {
314333
DECL_BINARY(&)
315334
DECL_BINARY(^)
316335
#undef DECL_BINARY
317-
/* Binary ops >> and << between a strong number type and integer constant.
318-
* The shift amount must fit into an u8 regardless of the left-side type.
336+
/* Binary ops >> and << between a strong number type and weak integer.
337+
* Unlike the other operations, these don't require the weak integer to be
338+
* constant because the result type is always the same as the left-side
339+
* operand.
319340
*/
320341
#define DECL_BINARY(op) \
321-
[[nodiscard]] force_inline friend constexpr \
322-
auto operator op(SelfType const &lhs, ConstantNum<std::uint8_t> const &rhs) noexcept \
323-
{ return SelfType{static_cast<T>(lhs.c_val op rhs.c_val)}; }
342+
template<std::unsigned_integral U> [[nodiscard]] force_inline friend \
343+
constexpr auto operator op(SelfType const &lhs, U const &rhs) noexcept \
344+
{ return SelfType{static_cast<T>(lhs.c_val op rhs)}; }
324345
DECL_BINARY(>>)
325346
DECL_BINARY(<<)
326347
#undef DECL_BINARY
@@ -511,16 +532,30 @@ using usize = std::size_t;
511532
using f32 = float;
512533
using f64 = double;
513534

535+
struct UInt : al::number_base<unsigned, UInt> { using number_base::number_base; using number_base::operator=; };
536+
template<typename CharT> struct al::formatter<UInt, CharT> : UInt::formatter<CharT> { };
537+
538+
namespace al {
539+
540+
template<weak_number T, typename SelfType>
541+
requires(not std::is_const_v<T> and not std::is_volatile_v<T>) [[nodiscard]] constexpr
542+
auto number_base<T,SelfType>::popcount() const noexcept -> UInt requires(std::integral<T>)
543+
{
544+
using unsigned_t = std::make_unsigned_t<T>;
545+
return UInt{static_cast<unsigned>(std::popcount(static_cast<unsigned_t>(c_val)))};
546+
}
547+
548+
} /* namespace al */
514549

515550
[[nodiscard]] consteval
516-
auto operator ""_i8(unsigned long long const n) noexcept { return i8{n}; }
551+
auto operator ""_i8(unsigned long long const n) noexcept { return i8::make_from(n); }
517552
[[nodiscard]] consteval
518-
auto operator ""_u8(unsigned long long const n) noexcept { return u8{n}; }
553+
auto operator ""_u8(unsigned long long const n) noexcept { return u8::make_from(n); }
519554

520555
[[nodiscard]] consteval
521-
auto operator ""_i16(unsigned long long const n) noexcept { return i16{n}; }
556+
auto operator ""_i16(unsigned long long const n) noexcept { return i16::make_from(n); }
522557
[[nodiscard]] consteval
523-
auto operator ""_u16(unsigned long long const n) noexcept { return u16{n}; }
558+
auto operator ""_u16(unsigned long long const n) noexcept { return u16::make_from(n); }
524559

525560
[[nodiscard]] consteval
526561
auto operator ""_i32(unsigned long long const n) noexcept { return gsl::narrow<i32>(n); }

core/device.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ struct MixParams {
136136
{
137137
if(AmbiMap[j].Index == inmix.AmbiMap[i].Index)
138138
{
139-
idx = u8{j};
139+
idx = u8{static_cast<u8::value_t>(j)};
140140
gain = AmbiMap[j].Scale * gainbase;
141141
break;
142142
}

core/hrtf.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -587,7 +587,10 @@ try {
587587
hrtf->mDelays.size()};
588588
std::ranges::transform(new_delays, (delays | std::views::join).begin(),
589589
[delay_scale](f32 const fdelay) -> u8
590-
{ return gsl::narrow_cast<u8>(float2int(fdelay*delay_scale + 0.5f)); });
590+
{
591+
return u8::make_from(al::saturate_cast<u8::value_t>(
592+
float2int(fdelay*delay_scale + 0.5f)));
593+
});
591594

592595
/* Scale the IR size for the new sample rate and update the stored
593596
* sample rate.

core/hrtf_loader.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,12 @@ auto readle(std::istream &data) -> T
165165

166166
alignas(T) auto ret = std::array<char,sizeof(T)>{};
167167
if(!data.read(ret.data(), num_bits/8))
168-
return gsl::narrow_cast<T>(EOF);
168+
{
169+
if constexpr(al::strong_number<T>)
170+
return T{gsl::narrow_cast<typename T::value_t>(EOF)};
171+
else
172+
return gsl::narrow_cast<T>(EOF);
173+
}
169174
if constexpr(std::endian::native == std::endian::big)
170175
std::reverse(ret.begin(), ret.end());
171176

examples/allafplay.cpp

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -410,12 +410,12 @@ auto LafStream::readChunk() -> u32
410410
}
411411

412412
mEnabledTracks = std::bit_cast<decltype(mEnabledTracks)>(enableTrackBits);
413-
mNumEnabled = gsl::narrow<u32>(std::accumulate(mEnabledTracks.cbegin(),
414-
mEnabledTracks.cend(), 0, [](int const val, u8 const in) -> int
415-
{ return val + std::popcount(in.c_val); }));
413+
mNumEnabled = std::accumulate(mEnabledTracks.cbegin(), mEnabledTracks.cend(),
414+
UInt{0u}, [](UInt const val, u8 const in) -> UInt
415+
{ return val + in.popcount(); }).c_val;
416416

417417
/* Make sure enable bits aren't set for non-existent tracks. */
418-
if(mNumEnabled > 0 && mEnabledTracks[((mNumTracks+7_uz)>>3) - 1] >= 1u<<(mNumTracks&7))
418+
if(mNumEnabled > 0 && mEnabledTracks[((mNumTracks+7_uz)>>3) - 1] >= 1_u8<<(mNumTracks&7))
419419
throw std::runtime_error{"Invalid channel enable bits"};
420420

421421
/* Each chunk is exactly one second long, with samples interleaved for each
@@ -453,18 +453,18 @@ auto LafStream::readChunk() -> u32
453453
auto LafStream::prepareTrack(usize const trackidx, usize const count) -> std::span<std::byte>
454454
{
455455
auto const todo = std::min(usize{mSampleRate}, count);
456-
if((mEnabledTracks[trackidx>>3] & u8{1<<(trackidx&7)}) != 0)
456+
if((mEnabledTracks[trackidx>>3] & (1_u8<<(trackidx&7))) != 0)
457457
{
458458
/* If the track is enabled, get the real index (skipping disabled
459459
* tracks), and deinterlace it into the mono line.
460460
*/
461461
auto const idx = std::invoke([this,trackidx]() -> u32
462462
{
463463
auto const bits = std::span{mEnabledTracks}.first(trackidx>>3);
464-
auto const res = std::accumulate(bits.begin(), bits.end(), 0_i32,
465-
[](int const val, u8 const in) -> int { return val + std::popcount(in.c_val); })
466-
+ std::popcount((mEnabledTracks[trackidx>>3] & u8{(1u<<(trackidx&7))-1}).c_val);
467-
return gsl::narrow_cast<u32>(res);
464+
auto const res = std::accumulate(bits.begin(), bits.end(), UInt{0},
465+
[](UInt const val, u8 const in) -> UInt { return val + in.popcount(); })
466+
+ (mEnabledTracks[trackidx>>3] & ((1_u8<<(trackidx&7))-1)).popcount();
467+
return gsl::narrow_cast<u32>(res.c_val);
468468
});
469469

470470
auto const step = usize{mNumEnabled};

utils/uhjdecoder.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -425,13 +425,13 @@ auto main(std::span<std::string_view> args) -> int
425425
// 16-bit val, format type id (extensible: 0xFFFE)
426426
fwrite16le(0xFFFE_u16, outfile);
427427
// 16-bit val, channel count
428-
fwrite16le(u16{outchans}, outfile);
428+
fwrite16le(u16::make_from(outchans), outfile);
429429
// 32-bit val, frequency
430430
fwrite32le(gsl::narrow<u32>(ininfo.samplerate), outfile);
431431
// 32-bit val, bytes per second
432432
fwrite32le(gsl::narrow<u32>(ininfo.samplerate)*outchans*u32{sizeof(f32)}, outfile);
433433
// 16-bit val, frame size
434-
fwrite16le(u16{sizeof(f32)*outchans}, outfile);
434+
fwrite16le(u16::make_from(sizeof(f32)*outchans), outfile);
435435
// 16-bit val, bits per sample
436436
fwrite16le(u16{sizeof(f32)*8}, outfile);
437437
// 16-bit val, extra byte count

0 commit comments

Comments
 (0)