Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[core] Functions returning arrays inconsistently use unsigned int/int for sizes #3168

Closed
Not-Nik opened this issue Jul 11, 2023 · 12 comments
Closed

Comments

@Not-Nik
Copy link
Sponsor Contributor

Not-Nik commented Jul 11, 2023

Currently, LoadFiledData uses an unsigned int to store the size of what it returns:

RLAPI unsigned char *LoadFileData(const char *fileName, unsigned int *bytesRead);

This is the sensible option in my opinion.

However, CompressData, DecompressData, EncodeDataBase64, DecodeDataBase64 and CodepointToUTF8 all use a normal signed int for their array sizes:

RLAPI unsigned char *CompressData(const unsigned char *data, int dataSize, int *compDataSize);
RLAPI unsigned char *DecompressData(const unsigned char *compData, int compDataSize, int *dataSize);
RLAPI char *EncodeDataBase64(const unsigned char *data, int dataSize, int *outputSize);
RLAPI unsigned char *DecodeDataBase64(const unsigned char *data, int *outputSize);
RLAPI const char *CodepointToUTF8(int codepoint, int *utf8Size);

Let's make this more consistent!

I can create a PR, but you'll need to tell me which of these you prefer. raylib uses signed integers for sizes all over the place so maybe it's time to make some bigger changes in that area; they wouldn't really break existing code and make the library more robust.

@raysan5
Copy link
Owner

raysan5 commented Jul 12, 2023

This change could also imply a list of additional changes for consistency and a bunch of type-missmatch warnings...

Here some possible sensible functions that could also require reviewing (but there could be even more):

int GetFileLength(const char *fileName);
int GetPixelDataSize(int width, int height, int format);

Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, int dataSize);
unsigned char *ExportImageToMemory(Image image, const char *fileType, int *fileSize);

Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int dataSize, int fontSize...
GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSize, int *fontChars...

void UpdateMeshBuffer(Mesh mesh, int index, const void *data, int dataSize, int offset);

Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int dataSize);
Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, int dataSize);
void SetAudioStreamBufferSizeDefault(int size);

Surprisingly, most compression/decompression libraries I checked used just plain int for sizes, I don't know why and I'd like to know it, maybe there is some reason to avoid using unsigned int?

What are the problems related to using just int?

@Not-Nik
Copy link
Sponsor Contributor Author

Not-Nik commented Jul 12, 2023

I also found, but forgot to list

RLAPI int *LoadCodepoints(const char *text, int *count);
RLAPI const char **TextSplit(const char *text, char delimiter, int *count);
RLAPI Material *LoadMaterials(const char *fileName, int *materialCount);
// This actually does use unsigned ints
RLAPI ModelAnimation *LoadModelAnimations(const char *fileName, unsigned int *animCount);

I think plain int can be set to -1 to indicate that there was an unrecoverable error, unlike with unsigned int where the best chance to indicate failure is to set the size to 0. This makes sense for general purpose applications where it's vital to properly indicate failure so that specific actions can be taken to mitigate the error. In game development, however, and especially in such a beginner/learning focused library as raylib, I feel like we can get away with just using some hardcoded default in case we can't load assets. This is already the case for fonts and textures/materials where there is a more or less sensible default that doesn't cause applications to crash or behave unreasonably unexpected.

Surprisingly, most compression/decompression libraries I checked used just plain int for sizes, I don't know why and I'd like to know it, maybe there is some reason to avoid using unsigned int?

zlib, the arguably most popular compression library, actually uses unsigned longs for sizes, but returns errors instead of returning values.

I don't think there are inherent problems with int, just that in an attempt at generalisation library developers opted to include error codes in these sizes.

@ghost
Copy link

ghost commented Jul 12, 2023

Surprisingly, most compression/decompression libraries I checked used just plain int for sizes, I don't know why and I'd like to know it, maybe there is some reason to avoid using unsigned int?

Perhaps it could be due to the possibility of overflow/underflow. Two good articles I have bookmarked here: "Pitfalls in C and C++: Unsigned types" and "C++ unsigned int problems".

@raysan5
Copy link
Owner

raysan5 commented Jul 12, 2023

@ubkp thanks for the articles, very interesting reads. I think they can be used for some specific cases (raw file/memory data?) but not for everything, I also consider that aesthetically, int looks way simpler to a newby than an unsigned int.

I need to think about it, there are too many functions involved in that change and many warnings to review.

@Not-Nik
Copy link
Sponsor Contributor Author

Not-Nik commented Jul 12, 2023

These articles make some good points. Either way I think it's important to make a choice and enforce it consistently.

@orcmid
Copy link
Contributor

orcmid commented Jul 12, 2023

@ubkp @Not-Nik I find the article to be ridiculous.

The TLDI: I am not proposing any reform to the APIs. Maybe better documentation, something that is not so traumatic.

OBSERVATIONS

First, overflow and underflow problems are the same, whether an int is signed or unsigned, they just occur differently. They wrap either way, because the same hardware operation is performed regardless in 2s-complement arithmetic.

There is a very big difference in comparisons though and that may or may not cause mysteries.

For example, in unsigned int cases, comparisons are very clear. It is also the case that adding one to the largest uint produces the smallest uint (namely 0). Adding one to the largest non-negative int produces the smallest (very-negative) int. Subtracting one from the smallest int produces ... guess what. It is an useful exercise to work out these cases and also the magical case that the smallest int is its own negative.

The use of int to also allow for exception cases is a serious relic in the C standard library (and its use in C++). This applies to exit() and also main(). It applies to most of the stdio.h operations and is probably better than checking errno elsewhere in the face of thread issues. I suggest that overloading the use of int in this manner is best confined to function results and not values communicated in and out of functions via parameters, at least in APIs of our own design.

I hazard that novices are not exposed to the above situations very much and I don't see much concern for checking int results from Standard Library functions either. Since these edge cases are wired into Standard C/C++ and modern processors, the question of how to protect novices and experts from unintended consequences is similar.

CONCLUSION

I don't think the problem at hand is the inescapable coyness of C/C++ integer representations. It is more about what a particular variable and type are intended to represent. That's why size_t is more descriptive (and portable) where it is used and applicable. Having a 0 value be possible is a pretty graceful fall-down (in contrast with a NULL pointer result).

We need to make clear what an int-typed (signed or unsigned) value is for and what the range expectations are. Ideally, we want a type that is the simplest suited to precisely that purpose.

PS: In the spirit of defensive programming, if an API uses an int parameter for where only non-negative values are acceptable, there is then an obligation to defend/detect/protect against an unwelcome value. This applies to ranges generally of course.

@makimy
Copy link

makimy commented Jul 14, 2023

No, the article "Pitfalls in C and C++: Unsigned types" is not rediculus.

First, overflow and underflow problems are the same, whether an int is signed or unsigned

It is not true.

@orcmid
Copy link
Contributor

orcmid commented Jul 14, 2023

@makimy

No, the article "Pitfalls in C and C++: Unsigned types" is not rediculus.

First, overflow and underflow problems are the same, whether an int is signed or unsigned

It is not true.

This is silly. I said they have the same problems, just in different ways.

uint's overflow and underflow by wrapping around. In the uint case, the wrap arounds are between 0 and UINTMAX. With int's the wraparounds are between INTMAX and INTMIN. You can wrap in either direction. And you can demonstrate on machines with 2's complement arithmetic, that -INTMIN == INTMIN. Since -0 == 0 (in 2's-complement) for int, there has to be at least one other case of a value that is its own negative.

Just try it. It is easy to demonstrate. Write a small program and satisfy yourself about this. #include <limits.h> to get the relevant minimum and maximum values. (There is no UINTMIN because 0 is easy enough.)

@makimy
Copy link

makimy commented Jul 14, 2023

Read the article carefully.

@orcmid
Copy link
Contributor

orcmid commented Jul 14, 2023

@makimy

Read the article carefully.

OK, I read it through again. I still think it is bogus. Yes, subtracting off the bottom of an unsigned element or assigning `-1' to an unsigned (which will involve a cast and should be detected unless the warning has been suppressed) is unfortunate.

The key thing is about having to know what are supported ranges and exercising care to avoid any undefended over-/under-flow cases. If our attention is on portability, that will matter the most. C Language gives us the problems, and also the tools to manage it once we get the hang of it.

PS: My concern with recommendations like those in the article is the significant risk that folks will blindly follow the recommendation as if there is then nothing to worry about. I have similar concerns with the casual use of float and hand-waving about how there will certainly be enough precision. Then there's the helplessness that accompanies a breakdown where those assumptions do not hold.

@makimy
Copy link

makimy commented Jul 14, 2023

I could start to write a great big comment about importance of performance in games (like nobody knows) , the bigger one about importance of portability (I use "thumb" on ARM by the way). Instead, I will say that I'm glad that you got it, the importance of writing valid, C99 code that raylib introduces as one of the main features, maybe, it does not follow it entirely, but it is ok.

@raysan5
Copy link
Owner

raysan5 commented Sep 2, 2023

Finally I decided to review all the functions to be consistent to int. It's probably the simplest type for a newby user, the resolution is not-that-distant to unsigned int (compared with bigger types) and it allows returning -1 in case of errors.

All parameters where also renamed to be consistent with dataSize.

@raysan5 raysan5 closed this as completed Sep 2, 2023
raysan5 added a commit that referenced this issue Sep 17, 2023
* Prettified a comment

* fixed broken indentation caused by another commit.
the commit renamed a bool to int and broke indentation: 233cf39

* Changed 0.001 and 0.00001 to EPSILON
This commit is untested.
I don't know what consequences this has.
Since the commits that added these numbers were before epsilon was added,
I have assumed that epsilon could replace them.

* Prettied up indentation in a few places

* removed spacing around *, standardizing it.

* I may have gotten overboard with indentation

* removed a few useless parenthesis

* Added fortran-raylib

* Fix examples/others/rlgl_standalone.c compilation issue (#3242)

* Update BINDINGS.md

* Ignore unused return value of GetCodepointNext in GetCodepointCount (#3241)

* Ignore unused return value of GetCodepointNext in GetCodepointCount

Removes the last warning from non-external libraries when compiling with
the default build configuration on x64 Linux.

* Remove unnecessary void cast in GetCodepointCount

* Fix #3246

* Revert "Fix #3246"

This reverts commit e4dcbd5.

* Fix text_unicode.c example crashing (#3250)

* Fix text_unicode.c example crashing

* Adjust the text_unicode.c example crashing fix

* tweaks

* add build.zig options for individual modules (#3254)

* Add `IsKeyPressedRepeat` (desktop only) (#3245)

Since the key pressed are handle by comparing current vs previous
state (ie frame), a special way is needed to handle key repeats.

* Reviewed `IsKeyPressedRepeat()` #3248

* Update rcore.c (#3255)

* Match CMakeOptions.txt options default values (#3258)

* Fix SetClipboardText for web (#3257)

* [Image] Validate that ImageDrawRectangleRec is drawing entirely inside the image (#3264)

* Add a function to clone a sound and share data with another sound.

* rename items based on feedback

* PR Feedback, use custom unload for sound alias, not variant of normal sound unloading

* sound_multi example

* Validate that image rect drawing is inside the image so we don't overflow a buffer

* remove files that should not have been added.

* remove changes that should not have been

* revert

* adsfasdfsdfsdf

* Add Vector3 Projecting and Rejection to Raymath (#3263)

* Update raymath.h

* formatting

* [Feature] IsKey... safety checks and more (#3256)

* [Feature] Add GetKeyRepeat

* Update rcore.c

* Simpler design, only one repeat per frame

* Update config.h

* Update rcore.c

* Add KEYBOARD_KEYS_MASK

* Update config.h

* reversions

* Update rcore.c

* Update rcore.c

* change docs

* Update rcore.c

* Update rcore.c

* Update rcore.c

* Update rcore.c

* Update rcore.c

* Update raylib.h

* Update rcore.c

* Update rcore.c

* Update rcore.c

* Update rcore.c

* Update rcore.c

* Update rcore.c

* Update rcore.c

* Update rcore.c

* Fix bug where default shaders was not linking. (#3261)

* Formating review

* Add missing cmake options (#3267)

* Fix CMake extraneous -lglfw (#3266)

Closes #3265.

The problem: LIBS_PRIVATE is a list of library names (used by pkg-config), but the shared library of the same name doesn't always exist.

* Fix example/models/models_loading_gltf.c controls (#3268)

* Fix example/models/models_loading_m3d.c controls (#3269)

* Remove e from secondes (#3270)

* Fix example/audio/audio_module_player.c help instructions and small bug (#3272)

* Fix example/audio/audio_module_player.c help instructions and small bug

* Update example/audio/audio_module_player.png screenshot

* Use type name instead of valid specifier

long long --> long long int

* REVIEWED: `GetFileLength()`, added comment #3262

* Update examples/models/models_loading_gltf.png;m3d.png screenshots (#3273)

* Remove a duplicated screenshot and add missing one (#3275)

* Add examples/shaders/shaders_lightmap.c to Makefiles (#3276)

* Fix examples/others/easings_testbed.c help instructions and small tweak (#3277)

* Fix examples/shaders/shaders_texture_outline.c help instructions (#3278)

* Fix examples/shapes/shapes_collision_area.c help instructions (#3279)

* RENAMED: LoadFont*() parameter names for consistency and coherence

* Fix uninitialized thread-locals in stbi #3282 (#3283)

* REVIEWED: Added `SetTextLineSpacing()` to multiline examples

* REVIEWED: Data size type consistency between functions #3168

* Some tweaks

* Use internal default allocators, instead of user-exposed ones

* Added rudimentary SVG support. (#2738)

* Added rudimentary SVG support. Added 2 functions ImageLoadSvg and ImageLoadSvgWithSize.

* Added an example on how to use ImageLoadSvgWithSize and adjusted Makefiles accordingly.

* Added actual correct example file.

* Reviewed the code to keep the raylib coding conventions in mind.
Moved the LoadImageSvg() code into LoadImage() guarded by SUPPORT_FILEFORMAT_SVG.
Renamed LoadImageSvgWithSize() to LoadImageSvg().
Added a LoadImageSvgFromString() function to parse the loaded SVG into an actual image. This does the bulk of the work.

* Fixed typo.

---------

Co-authored-by: Ray <[email protected]>

* REVIEWED: `LoadImageSvg()`

* REVIEWED: `LoadImageSvg()`

* Add SUPPORT_FILEFORMAT_SVG to cmake (#3284)

* Fix examples/textures/textures_fog_of_war.c help instructions (#3285)

* Fix examples/textures/textures_image_rotate.c help instructions (#3286)

* Update rtextures.c

* Fix #3247

* Update config.h

* Fix #3293

* Disable UBSAN in zig builds. (#3292)

Zig debug builds automatically enable ubsan.
As the fix for #1891 had to be reverted, debug builds using zig will crash like so:

```
Illegal instruction at address 0x3237d2
raylib/src/rlgl.h:3690:91: 0x3237d2 in rlDrawVertexArrayElements (/home/rcorre/src/raylib-zig-template/raylib/src/rcore.c)
    glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_SHORT, (const unsigned short *)buffer + offset);
```

This disables UBSAN when using zig to build raylib.

* Update README.md (#3290)

specially -> especially

* Update cmake SUPPORT_FILEFORMAT_SVG default value (#3291)

* Mouse offset and scaling must be considered also on web!

* Update rcore.c

* Update Makefile : clean raygui.c & physac.c (#3296)

* Remove PLATFORM_RPI (#3232)

* Remove PLATFORM_RPI

* remove build artifacts

---------

Co-authored-by: MichaelFiber <[email protected]>
Co-authored-by: Ray <[email protected]>

* Review to avoid UBSAN complaining #1891

* added raylib-raku to bindings (#3299)

* examples: core: adds 2D camera two player split screen (#3298)

* Reviewed examples for consistency

* Update rtext.c

* Some code restructuring for input functions, consistency review

* Remove unneeded #if (#3301)

Co-authored-by: MichaelFiber <[email protected]>

* Revert "Disable UBSAN in zig builds. (#3292)" (#3303)

This reverts commit a316f9e.

Issue #1891 was fixed again, so this is no longer needed.

* rtextures: Fix ImageDraw() source clipping when drawing beyond top left (#3306)

* REVIEWED: `TextToPascal()` issue when first char is uppercase

* Implement FLAG_WINDOW_RESIZABLE for web (#3305)

Fixes #3231

* Update BINDINGS.md (#3307)

Fix Kaylib binding. Reroute to a new repository.
Binding renamed.

* Update webassembly.yml

* Add claw-raylib to BINDINGS.md (#3310)

* Add SetWindowMaxSize for desktop and web (#3309)

* Add SetWindowMaxSize for desktop and web

* Remove SizeInt and respective adjustments

* Update rtextures.c

* Reviewed parameters for consistency

* Rename windowM* to screenM* (#3312)

* Update BINDINGS.md (#3317)

Update TurboRaylib bindings

* Update rmodels.c

* Update BINDINGS.md with vaiorabbit/raylib-bindings (#3318)

* fixed spelling mistake

* put back parenthesis

* reverted major allignment changes

* reverted parser output changes

* reverted one more indentation change

---------

Co-authored-by: Brian-E <[email protected]>
Co-authored-by: Ray <[email protected]>
Co-authored-by: ubkp <[email protected]>
Co-authored-by: ashn <[email protected]>
Co-authored-by: actondev (Christos) <[email protected]>
Co-authored-by: vitopigno <[email protected]>
Co-authored-by: Asdqwe <[email protected]>
Co-authored-by: Jeffery Myers <[email protected]>
Co-authored-by: Ethan Simpson <[email protected]>
Co-authored-by: Nickolas McDonald <[email protected]>
Co-authored-by: Branimir Ričko <[email protected]>
Co-authored-by: iacore <[email protected]>
Co-authored-by: Ethan Conneely <[email protected]>
Co-authored-by: Johannes Barthelmes <[email protected]>
Co-authored-by: bXi <[email protected]>
Co-authored-by: Ryan Roden-Corrent <[email protected]>
Co-authored-by: Ikko Eltociear Ashimine <[email protected]>
Co-authored-by: SuperUserNameMan <[email protected]>
Co-authored-by: MichaelFiber <[email protected]>
Co-authored-by: MichaelFiber <[email protected]>
Co-authored-by: Dan Vu <[email protected]>
Co-authored-by: Gabriel dos Santos Sanches <[email protected]>
Co-authored-by: Rob Loach <[email protected]>
Co-authored-by: Peter0x44 <[email protected]>
Co-authored-by: Kenta <[email protected]>
Co-authored-by: bohonghuang <[email protected]>
Co-authored-by: turborium <[email protected]>
Co-authored-by: Wilson Silva <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants