diff options
author | Siddhartha Jejurkar <f20180617@goa.bits-pilani.ac.in> | 2021-09-22 18:37:15 +0300 |
---|---|---|
committer | Siddhartha Jejurkar <f20180617@goa.bits-pilani.ac.in> | 2021-09-22 18:37:15 +0300 |
commit | 1b9dd08d0281f00ba96b2e6b74c7c9b662663c1b (patch) | |
tree | d771024f234cb9fc0984235174a254ed6c38d8c4 /source/blender/blenlib/intern | |
parent | 0c2bc843f500cc0bc9511b5ca68ab61a8922ce9b (diff) | |
parent | 9f6313498a0af386f08ed17c83bf33b8c2c3b5b3 (diff) |
Merge branch 'master' into soc-2021-uv-editor-improvements
Diffstat (limited to 'source/blender/blenlib/intern')
-rw-r--r-- | source/blender/blenlib/intern/fileops.c | 243 | ||||
-rw-r--r-- | source/blender/blenlib/intern/filereader_file.c | 80 | ||||
-rw-r--r-- | source/blender/blenlib/intern/filereader_gzip.c | 108 | ||||
-rw-r--r-- | source/blender/blenlib/intern/filereader_memory.c | 145 | ||||
-rw-r--r-- | source/blender/blenlib/intern/filereader_zstd.c | 335 | ||||
-rw-r--r-- | source/blender/blenlib/intern/freetypefont.c | 39 | ||||
-rw-r--r-- | source/blender/blenlib/intern/index_mask.cc | 57 | ||||
-rw-r--r-- | source/blender/blenlib/intern/math_base_inline.c | 20 | ||||
-rw-r--r-- | source/blender/blenlib/intern/noise.cc | 693 | ||||
-rw-r--r-- | source/blender/blenlib/intern/string.c | 194 | ||||
-rw-r--r-- | source/blender/blenlib/intern/string_cursor_utf8.c | 33 | ||||
-rw-r--r-- | source/blender/blenlib/intern/string_search.cc | 17 | ||||
-rw-r--r-- | source/blender/blenlib/intern/string_utf8.c | 364 | ||||
-rw-r--r-- | source/blender/blenlib/intern/string_utils.c | 17 | ||||
-rw-r--r-- | source/blender/blenlib/intern/uuid.cc | 139 | ||||
-rw-r--r-- | source/blender/blenlib/intern/winstuff_dir.c | 4 |
16 files changed, 2024 insertions, 464 deletions
diff --git a/source/blender/blenlib/intern/fileops.c b/source/blender/blenlib/intern/fileops.c index ac034d2b5cd..532a29b8147 100644 --- a/source/blender/blenlib/intern/fileops.c +++ b/source/blender/blenlib/intern/fileops.c @@ -31,6 +31,7 @@ #include <errno.h> #include "zlib.h" +#include "zstd.h" #ifdef WIN32 # include "BLI_fileops_types.h" @@ -61,200 +62,124 @@ #include "BLI_sys_types.h" /* for intptr_t support */ #include "BLI_utildefines.h" -#if 0 /* UNUSED */ -/* gzip the file in from and write it to "to". - * return -1 if zlib fails, -2 if the originating file does not exist - * NOTE: will remove the "from" file - */ -int BLI_file_gzip(const char *from, const char *to) +size_t BLI_file_zstd_from_mem_at_pos( + void *buf, size_t len, FILE *file, size_t file_offset, int compression_level) { - char buffer[10240]; - int file; - int readsize = 0; - int rval = 0, err; - gzFile gzfile; + fseek(file, file_offset, SEEK_SET); - /* level 1 is very close to 3 (the default) in terms of file size, - * but about twice as fast, best use for speedy saving - campbell */ - gzfile = BLI_gzopen(to, "wb1"); - if (gzfile == NULL) { - return -1; - } - file = BLI_open(from, O_BINARY | O_RDONLY, 0); - if (file == -1) { - return -2; - } + ZSTD_CCtx *ctx = ZSTD_createCCtx(); + ZSTD_CCtx_setParameter(ctx, ZSTD_c_compressionLevel, compression_level); + + ZSTD_inBuffer input = {buf, len, 0}; - while (1) { - readsize = read(file, buffer, sizeof(buffer)); + size_t out_len = ZSTD_CStreamOutSize(); + void *out_buf = MEM_mallocN(out_len, __func__); + size_t total_written = 0; - if (readsize < 0) { - rval = -2; /* error happened in reading */ - fprintf(stderr, "Error reading file %s: %s.\n", from, strerror(errno)); + /* Compress block and write it out until the input has been consumed. */ + while (input.pos < input.size) { + ZSTD_outBuffer output = {out_buf, out_len, 0}; + size_t ret = ZSTD_compressStream2(ctx, &output, &input, ZSTD_e_continue); + if (ZSTD_isError(ret)) { break; } - else if (readsize == 0) { - break; /* done reading */ + if (fwrite(out_buf, 1, output.pos, file) != output.pos) { + break; } + total_written += output.pos; + } - if (gzwrite(gzfile, buffer, readsize) <= 0) { - rval = -1; /* error happened in writing */ - fprintf(stderr, "Error writing gz file %s: %s.\n", to, gzerror(gzfile, &err)); + /* Finalize the Zstd frame. */ + size_t ret = 1; + while (ret != 0) { + ZSTD_outBuffer output = {out_buf, out_len, 0}; + ret = ZSTD_compressStream2(ctx, &output, &input, ZSTD_e_end); + if (ZSTD_isError(ret)) { + break; + } + if (fwrite(out_buf, 1, output.pos, file) != output.pos) { break; } + total_written += output.pos; } - gzclose(gzfile); - close(file); + MEM_freeN(out_buf); + ZSTD_freeCCtx(ctx); - return rval; + return ZSTD_isError(ret) ? 0 : total_written; } -#endif -/* gzip the file in from_file and write it to memory to_mem, at most size bytes. - * return the unzipped size - */ -char *BLI_file_ungzip_to_mem(const char *from_file, int *r_size) +size_t BLI_file_unzstd_to_mem_at_pos(void *buf, size_t len, FILE *file, size_t file_offset) { - gzFile gzfile; - int readsize, size, alloc_size = 0; - char *mem = NULL; - const int chunk_size = 512 * 1024; + fseek(file, file_offset, SEEK_SET); - size = 0; + ZSTD_DCtx *ctx = ZSTD_createDCtx(); - gzfile = BLI_gzopen(from_file, "rb"); - for (;;) { - if (mem == NULL) { - mem = MEM_callocN(chunk_size, "BLI_ungzip_to_mem"); - alloc_size = chunk_size; - } - else { - mem = MEM_reallocN(mem, size + chunk_size); - alloc_size += chunk_size; - } + size_t in_len = ZSTD_DStreamInSize(); + void *in_buf = MEM_mallocN(in_len, __func__); + ZSTD_inBuffer input = {in_buf, in_len, 0}; - readsize = gzread(gzfile, mem + size, chunk_size); - if (readsize > 0) { - size += readsize; - } - else { + ZSTD_outBuffer output = {buf, len, 0}; + + size_t ret = 0; + /* Read and decompress chunks of input data until we have enough output. */ + while (output.pos < output.size && !ZSTD_isError(ret)) { + input.size = fread(in_buf, 1, in_len, file); + if (input.size == 0) { break; } - } - gzclose(gzfile); + /* Consume input data until we run out or have enough output. */ + input.pos = 0; + while (input.pos < input.size && output.pos < output.size) { + ret = ZSTD_decompressStream(ctx, &output, &input); - if (size == 0) { - MEM_freeN(mem); - mem = NULL; - } - else if (alloc_size != size) { - mem = MEM_reallocN(mem, size); + if (ZSTD_isError(ret)) { + break; + } + } } - *r_size = size; + MEM_freeN(in_buf); + ZSTD_freeDCtx(ctx); - return mem; + return ZSTD_isError(ret) ? 0 : output.pos; } -#define CHUNK (256 * 1024) - -/* gzip byte array from memory and write it to file at certain position. - * return size of gzip stream. - */ -size_t BLI_gzip_mem_to_file_at_pos( - void *buf, size_t len, FILE *file, size_t gz_stream_offset, int compression_level) +bool BLI_file_magic_is_gzip(const char header[4]) { - int ret, flush; - unsigned have; - z_stream strm; - unsigned char out[CHUNK]; - - BLI_fseek(file, gz_stream_offset, 0); - - strm.zalloc = Z_NULL; - strm.zfree = Z_NULL; - strm.opaque = Z_NULL; - ret = deflateInit(&strm, compression_level); - if (ret != Z_OK) { - return 0; - } - - strm.avail_in = len; - strm.next_in = (Bytef *)buf; - flush = Z_FINISH; - - do { - strm.avail_out = CHUNK; - strm.next_out = out; - ret = deflate(&strm, flush); - if (ret == Z_STREAM_ERROR) { - return 0; - } - have = CHUNK - strm.avail_out; - if (fwrite(out, 1, have, file) != have || ferror(file)) { - deflateEnd(&strm); - return 0; - } - } while (strm.avail_out == 0); - - if (strm.avail_in != 0 || ret != Z_STREAM_END) { - return 0; - } - - deflateEnd(&strm); - return (size_t)strm.total_out; + /* GZIP itself starts with the magic bytes 0x1f 0x8b. + * The third byte indicates the compression method, which is 0x08 for DEFLATE. */ + return header[0] == 0x1f && header[1] == 0x8b && header[2] == 0x08; } -/* read and decompress gzip stream from file at certain position to buffer. - * return size of decompressed data. - */ -size_t BLI_ungzip_file_to_mem_at_pos(void *buf, size_t len, FILE *file, size_t gz_stream_offset) +bool BLI_file_magic_is_zstd(const char header[4]) { - int ret; - z_stream strm; - size_t chunk = 256 * 1024; - unsigned char in[CHUNK]; - - BLI_fseek(file, gz_stream_offset, 0); - - strm.zalloc = Z_NULL; - strm.zfree = Z_NULL; - strm.opaque = Z_NULL; - strm.avail_in = 0; - strm.next_in = Z_NULL; - ret = inflateInit(&strm); - if (ret != Z_OK) { - return 0; + /* ZSTD files consist of concatenated frames, each either a Zstd frame or a skippable frame. + * Both types of frames start with a magic number: 0xFD2FB528 for Zstd frames and 0x184D2A5* + * for skippable frames, with the * being anything from 0 to F. + * + * To check whether a file is Zstd-compressed, we just check whether the first frame matches + * either. Seeking through the file until a Zstd frame is found would make things more + * complicated and the probability of a false positive is rather low anyways. + * + * Note that LZ4 uses a compatible format, so even though its compressed frames have a + * different magic number, a valid LZ4 file might also start with a skippable frame matching + * the second check here. + * + * For more details, see https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md + */ + + uint32_t magic = *((uint32_t *)header); + if (magic == 0xFD2FB528) { + return true; } - - do { - strm.avail_in = fread(in, 1, chunk, file); - strm.next_in = in; - if (ferror(file)) { - inflateEnd(&strm); - return 0; - } - - do { - strm.avail_out = len; - strm.next_out = (Bytef *)buf + strm.total_out; - - ret = inflate(&strm, Z_NO_FLUSH); - if (ret == Z_STREAM_ERROR) { - return 0; - } - } while (strm.avail_out == 0); - - } while (ret != Z_STREAM_END); - - inflateEnd(&strm); - return (size_t)strm.total_out; + if ((magic >> 4) == 0x184D2A5) { + return true; + } + return false; } -#undef CHUNK - /** * Returns true if the file with the specified name can be written. * This implementation uses access(2), which makes the check according diff --git a/source/blender/blenlib/intern/filereader_file.c b/source/blender/blenlib/intern/filereader_file.c new file mode 100644 index 00000000000..3a833871e27 --- /dev/null +++ b/source/blender/blenlib/intern/filereader_file.c @@ -0,0 +1,80 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2004-2021 Blender Foundation + * All rights reserved. + */ + +/** \file + * \ingroup bli + */ + +#ifndef WIN32 +# include <unistd.h> /* for read close */ +#else +# include "BLI_winstuff.h" +# include "winsock2.h" +# include <io.h> /* for open close read */ +#endif + +#include "BLI_blenlib.h" +#include "BLI_filereader.h" + +#include "MEM_guardedalloc.h" + +typedef struct { + FileReader reader; + + int filedes; +} RawFileReader; + +static ssize_t file_read(FileReader *reader, void *buffer, size_t size) +{ + RawFileReader *rawfile = (RawFileReader *)reader; + ssize_t readsize = read(rawfile->filedes, buffer, size); + + if (readsize >= 0) { + rawfile->reader.offset += readsize; + } + + return readsize; +} + +static off64_t file_seek(FileReader *reader, off64_t offset, int whence) +{ + RawFileReader *rawfile = (RawFileReader *)reader; + rawfile->reader.offset = BLI_lseek(rawfile->filedes, offset, whence); + return rawfile->reader.offset; +} + +static void file_close(FileReader *reader) +{ + RawFileReader *rawfile = (RawFileReader *)reader; + close(rawfile->filedes); + MEM_freeN(rawfile); +} + +FileReader *BLI_filereader_new_file(int filedes) +{ + RawFileReader *rawfile = MEM_callocN(sizeof(RawFileReader), __func__); + + rawfile->filedes = filedes; + + rawfile->reader.read = file_read; + rawfile->reader.seek = file_seek; + rawfile->reader.close = file_close; + + return (FileReader *)rawfile; +} diff --git a/source/blender/blenlib/intern/filereader_gzip.c b/source/blender/blenlib/intern/filereader_gzip.c new file mode 100644 index 00000000000..72eb153a8b9 --- /dev/null +++ b/source/blender/blenlib/intern/filereader_gzip.c @@ -0,0 +1,108 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2004-2021 Blender Foundation + * All rights reserved. + */ + +/** \file + * \ingroup bli + */ + +#include <zlib.h> + +#include "BLI_blenlib.h" +#include "BLI_filereader.h" + +#include "MEM_guardedalloc.h" + +typedef struct { + FileReader reader; + + FileReader *base; + + z_stream strm; + + void *in_buf; + size_t in_size; +} GzipReader; + +static ssize_t gzip_read(FileReader *reader, void *buffer, size_t size) +{ + GzipReader *gzip = (GzipReader *)reader; + + gzip->strm.avail_out = size; + gzip->strm.next_out = buffer; + + while (gzip->strm.avail_out > 0) { + if (gzip->strm.avail_in == 0) { + /* Ran out of buffered input data, read some more. */ + size_t readsize = gzip->base->read(gzip->base, gzip->in_buf, gzip->in_size); + + if (readsize > 0) { + /* We got some data, so mark the buffer as refilled. */ + gzip->strm.avail_in = readsize; + gzip->strm.next_in = gzip->in_buf; + } + else { + /* The underlying file is EOF, so return as much as we can. */ + break; + } + } + + int ret = inflate(&gzip->strm, Z_NO_FLUSH); + + if (ret != Z_OK && ret != Z_BUF_ERROR) { + break; + } + } + + ssize_t read_len = size - gzip->strm.avail_out; + gzip->reader.offset += read_len; + return read_len; +} + +static void gzip_close(FileReader *reader) +{ + GzipReader *gzip = (GzipReader *)reader; + + if (inflateEnd(&gzip->strm) != Z_OK) { + printf("close gzip stream error\n"); + } + MEM_freeN((void *)gzip->in_buf); + + gzip->base->close(gzip->base); + MEM_freeN(gzip); +} + +FileReader *BLI_filereader_new_gzip(FileReader *base) +{ + GzipReader *gzip = MEM_callocN(sizeof(GzipReader), __func__); + gzip->base = base; + + if (inflateInit2(&gzip->strm, 16 + MAX_WBITS) != Z_OK) { + MEM_freeN(gzip); + return NULL; + } + + gzip->in_size = 256 * 2014; + gzip->in_buf = MEM_mallocN(gzip->in_size, "gzip in buf"); + + gzip->reader.read = gzip_read; + gzip->reader.seek = NULL; + gzip->reader.close = gzip_close; + + return (FileReader *)gzip; +} diff --git a/source/blender/blenlib/intern/filereader_memory.c b/source/blender/blenlib/intern/filereader_memory.c new file mode 100644 index 00000000000..150fed7d1cc --- /dev/null +++ b/source/blender/blenlib/intern/filereader_memory.c @@ -0,0 +1,145 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2004-2021 Blender Foundation + * All rights reserved. + */ + +/** \file + * \ingroup bli + */ + +#include <string.h> + +#include "BLI_blenlib.h" +#include "BLI_filereader.h" +#include "BLI_mmap.h" + +#include "MEM_guardedalloc.h" + +/* This file implements both memory-backed and memory-mapped-file-backed reading. */ +typedef struct { + FileReader reader; + + const char *data; + BLI_mmap_file *mmap; + size_t length; +} MemoryReader; + +static ssize_t memory_read_raw(FileReader *reader, void *buffer, size_t size) +{ + MemoryReader *mem = (MemoryReader *)reader; + + /* Don't read more bytes than there are available in the buffer. */ + size_t readsize = MIN2(size, (size_t)(mem->length - mem->reader.offset)); + + memcpy(buffer, mem->data + mem->reader.offset, readsize); + mem->reader.offset += readsize; + + return readsize; +} + +static off64_t memory_seek(FileReader *reader, off64_t offset, int whence) +{ + MemoryReader *mem = (MemoryReader *)reader; + + off64_t new_pos; + if (whence == SEEK_CUR) { + new_pos = mem->reader.offset + offset; + } + else if (whence == SEEK_SET) { + new_pos = offset; + } + else if (whence == SEEK_END) { + new_pos = mem->length + offset; + } + else { + return -1; + } + + if (new_pos < 0 || new_pos > mem->length) { + return -1; + } + + mem->reader.offset = new_pos; + return mem->reader.offset; +} + +static void memory_close_raw(FileReader *reader) +{ + MEM_freeN(reader); +} + +FileReader *BLI_filereader_new_memory(const void *data, size_t len) +{ + MemoryReader *mem = MEM_callocN(sizeof(MemoryReader), __func__); + + mem->data = (const char *)data; + mem->length = len; + + mem->reader.read = memory_read_raw; + mem->reader.seek = memory_seek; + mem->reader.close = memory_close_raw; + + return (FileReader *)mem; +} + +/* Memory-mapped file reading. + * By using `mmap()`, we can map a file so that it can be treated like normal memory, + * meaning that we can just read from it with `memcpy()` etc. + * This avoids system call overhead and can significantly speed up file loading. + */ + +static ssize_t memory_read_mmap(FileReader *reader, void *buffer, size_t size) +{ + MemoryReader *mem = (MemoryReader *)reader; + + /* Don't read more bytes than there are available in the buffer. */ + size_t readsize = MIN2(size, (size_t)(mem->length - mem->reader.offset)); + + if (!BLI_mmap_read(mem->mmap, buffer, mem->reader.offset, readsize)) { + return 0; + } + + mem->reader.offset += readsize; + + return readsize; +} + +static void memory_close_mmap(FileReader *reader) +{ + MemoryReader *mem = (MemoryReader *)reader; + BLI_mmap_free(mem->mmap); + MEM_freeN(mem); +} + +FileReader *BLI_filereader_new_mmap(int filedes) +{ + BLI_mmap_file *mmap = BLI_mmap_open(filedes); + if (mmap == NULL) { + return NULL; + } + + MemoryReader *mem = MEM_callocN(sizeof(MemoryReader), __func__); + + mem->mmap = mmap; + mem->length = BLI_lseek(filedes, 0, SEEK_END); + + mem->reader.read = memory_read_mmap; + mem->reader.seek = memory_seek; + mem->reader.close = memory_close_mmap; + + return (FileReader *)mem; +} diff --git a/source/blender/blenlib/intern/filereader_zstd.c b/source/blender/blenlib/intern/filereader_zstd.c new file mode 100644 index 00000000000..55ce32713d9 --- /dev/null +++ b/source/blender/blenlib/intern/filereader_zstd.c @@ -0,0 +1,335 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2021 Blender Foundation + * All rights reserved. + */ + +/** \file + * \ingroup bli + */ + +#include <string.h> +#include <zstd.h> + +#include "BLI_blenlib.h" +#include "BLI_endian_switch.h" +#include "BLI_filereader.h" +#include "BLI_math_base.h" + +#include "MEM_guardedalloc.h" + +typedef struct { + FileReader reader; + + FileReader *base; + + ZSTD_DCtx *ctx; + ZSTD_inBuffer in_buf; + size_t in_buf_max_size; + + struct { + int num_frames; + size_t *compressed_ofs; + size_t *uncompressed_ofs; + + char *cached_content; + int cached_frame; + } seek; +} ZstdReader; + +static bool zstd_read_u32(FileReader *base, uint32_t *val) +{ + if (base->read(base, val, sizeof(uint32_t)) != sizeof(uint32_t)) { + return false; + } +#ifdef __BIG_ENDIAN__ + BLI_endian_switch_uint32(val); +#endif + return true; +} + +static bool zstd_read_seek_table(ZstdReader *zstd) +{ + FileReader *base = zstd->base; + + /* The seek table frame is at the end of the file, so seek there + * and verify that there is enough data. */ + if (base->seek(base, -4, SEEK_END) < 13) { + return false; + } + uint32_t magic; + if (!zstd_read_u32(base, &magic) || magic != 0x8F92EAB1) { + return false; + } + + uint8_t flags; + if (base->seek(base, -5, SEEK_END) < 0 || base->read(base, &flags, 1) != 1) { + return false; + } + /* Bit 7 indicates check-sums. Bits 5 and 6 must be zero. */ + bool has_checksums = (flags & 0x80); + if (flags & 0x60) { + return false; + } + + uint32_t num_frames; + if (base->seek(base, -9, SEEK_END) < 0 || !zstd_read_u32(base, &num_frames)) { + return false; + } + + /* Each frame has either 2 or 3 uint32_t, and after that we have + * num_frames, flags and magic for another 9 bytes. */ + uint32_t expected_frame_length = num_frames * (has_checksums ? 12 : 8) + 9; + /* The frame starts with another magic number and its length, but these + * two fields are not included when counting length. */ + off64_t frame_start_ofs = 8 + expected_frame_length; + /* Sanity check: Before the start of the seek table frame, + * there must be num_frames frames, each of which at least 8 bytes long. */ + off64_t seek_frame_start = base->seek(base, -frame_start_ofs, SEEK_END); + if (seek_frame_start < num_frames * 8) { + return false; + } + + if (!zstd_read_u32(base, &magic) || magic != 0x184D2A5E) { + return false; + } + + uint32_t frame_length; + if (!zstd_read_u32(base, &frame_length) || frame_length != expected_frame_length) { + return false; + } + + zstd->seek.num_frames = num_frames; + zstd->seek.compressed_ofs = MEM_malloc_arrayN(num_frames + 1, sizeof(size_t), __func__); + zstd->seek.uncompressed_ofs = MEM_malloc_arrayN(num_frames + 1, sizeof(size_t), __func__); + + size_t compressed_ofs = 0; + size_t uncompressed_ofs = 0; + for (int i = 0; i < num_frames; i++) { + uint32_t compressed_size, uncompressed_size; + if (!zstd_read_u32(base, &compressed_size) || !zstd_read_u32(base, &uncompressed_size)) { + break; + } + if (has_checksums && base->seek(base, 4, SEEK_CUR) < 0) { + break; + } + zstd->seek.compressed_ofs[i] = compressed_ofs; + zstd->seek.uncompressed_ofs[i] = uncompressed_ofs; + compressed_ofs += compressed_size; + uncompressed_ofs += uncompressed_size; + } + zstd->seek.compressed_ofs[num_frames] = compressed_ofs; + zstd->seek.uncompressed_ofs[num_frames] = uncompressed_ofs; + + /* Seek to the end of the previous frame for the following #BHead frame detection. */ + if (seek_frame_start != compressed_ofs || base->seek(base, seek_frame_start, SEEK_SET) < 0) { + MEM_freeN(zstd->seek.compressed_ofs); + MEM_freeN(zstd->seek.uncompressed_ofs); + memset(&zstd->seek, 0, sizeof(zstd->seek)); + return false; + } + + zstd->seek.cached_frame = -1; + + return true; +} + +/* Find out which frame contains the given position in the uncompressed stream. + * Basically just bisection. */ +static int zstd_frame_from_pos(ZstdReader *zstd, size_t pos) +{ + int low = 0, high = zstd->seek.num_frames; + + if (pos >= zstd->seek.uncompressed_ofs[zstd->seek.num_frames]) { + return -1; + } + + while (low + 1 < high) { + int mid = low + ((high - low) >> 1); + if (zstd->seek.uncompressed_ofs[mid] <= pos) { + low = mid; + } + else { + high = mid; + } + } + + return low; +} + +/* Ensure that the currently loaded frame is the correct one. */ +static const char *zstd_ensure_cache(ZstdReader *zstd, int frame) +{ + if (zstd->seek.cached_frame == frame) { + /* Cached frame matches, so just return it. */ + return zstd->seek.cached_content; + } + + /* Cached frame doesn't match, so discard it and cache the wanted one instead. */ + MEM_SAFE_FREE(zstd->seek.cached_content); + + size_t compressed_size = zstd->seek.compressed_ofs[frame + 1] - zstd->seek.compressed_ofs[frame]; + size_t uncompressed_size = zstd->seek.uncompressed_ofs[frame + 1] - + zstd->seek.uncompressed_ofs[frame]; + + char *uncompressed_data = MEM_mallocN(uncompressed_size, __func__); + char *compressed_data = MEM_mallocN(compressed_size, __func__); + if (zstd->base->seek(zstd->base, zstd->seek.compressed_ofs[frame], SEEK_SET) < 0 || + zstd->base->read(zstd->base, compressed_data, compressed_size) < compressed_size) { + MEM_freeN(compressed_data); + MEM_freeN(uncompressed_data); + return NULL; + } + + size_t res = ZSTD_decompressDCtx( + zstd->ctx, uncompressed_data, uncompressed_size, compressed_data, compressed_size); + MEM_freeN(compressed_data); + if (ZSTD_isError(res) || res < uncompressed_size) { + MEM_freeN(uncompressed_data); + return NULL; + } + + zstd->seek.cached_frame = frame; + zstd->seek.cached_content = uncompressed_data; + return uncompressed_data; +} + +static ssize_t zstd_read_seekable(FileReader *reader, void *buffer, size_t size) +{ + ZstdReader *zstd = (ZstdReader *)reader; + + size_t end_offset = zstd->reader.offset + size, read_len = 0; + while (zstd->reader.offset < end_offset) { + int frame = zstd_frame_from_pos(zstd, zstd->reader.offset); + if (frame < 0) { + /* EOF is reached, so return as much as we can. */ + break; + } + + const char *framedata = zstd_ensure_cache(zstd, frame); + if (framedata == NULL) { + /* Error while reading the frame, so return as much as we can. */ + break; + } + + size_t frame_end_offset = min_zz(zstd->seek.uncompressed_ofs[frame + 1], end_offset); + size_t frame_read_len = frame_end_offset - zstd->reader.offset; + + size_t offset_in_frame = zstd->reader.offset - zstd->seek.uncompressed_ofs[frame]; + memcpy((char *)buffer + read_len, framedata + offset_in_frame, frame_read_len); + read_len += frame_read_len; + zstd->reader.offset = frame_end_offset; + } + + return read_len; +} + +static off64_t zstd_seek(FileReader *reader, off64_t offset, int whence) +{ + ZstdReader *zstd = (ZstdReader *)reader; + off64_t new_pos; + if (whence == SEEK_SET) { + new_pos = offset; + } + else if (whence == SEEK_END) { + new_pos = zstd->seek.uncompressed_ofs[zstd->seek.num_frames] + offset; + } + else { + new_pos = zstd->reader.offset + offset; + } + + if (new_pos < 0 || new_pos > zstd->seek.uncompressed_ofs[zstd->seek.num_frames]) { + return -1; + } + zstd->reader.offset = new_pos; + return zstd->reader.offset; +} + +static ssize_t zstd_read(FileReader *reader, void *buffer, size_t size) +{ + ZstdReader *zstd = (ZstdReader *)reader; + ZSTD_outBuffer output = {buffer, size, 0}; + + while (output.pos < output.size) { + if (zstd->in_buf.pos == zstd->in_buf.size) { + /* Ran out of buffered input data, read some more. */ + zstd->in_buf.pos = 0; + ssize_t readsize = zstd->base->read( + zstd->base, (char *)zstd->in_buf.src, zstd->in_buf_max_size); + + if (readsize > 0) { + /* We got some data, so mark the buffer as refilled. */ + zstd->in_buf.size = readsize; + } + else { + /* The underlying file is EOF, so return as much as we can. */ + break; + } + } + + if (ZSTD_isError(ZSTD_decompressStream(zstd->ctx, &output, &zstd->in_buf))) { + break; + } + } + + zstd->reader.offset += output.pos; + return output.pos; +} + +static void zstd_close(FileReader *reader) +{ + ZstdReader *zstd = (ZstdReader *)reader; + + ZSTD_freeDCtx(zstd->ctx); + if (zstd->reader.seek) { + MEM_freeN(zstd->seek.uncompressed_ofs); + MEM_freeN(zstd->seek.compressed_ofs); + MEM_freeN(zstd->seek.cached_content); + } + else { + MEM_freeN((void *)zstd->in_buf.src); + } + + zstd->base->close(zstd->base); + MEM_freeN(zstd); +} + +FileReader *BLI_filereader_new_zstd(FileReader *base) +{ + ZstdReader *zstd = MEM_callocN(sizeof(ZstdReader), __func__); + + zstd->ctx = ZSTD_createDCtx(); + zstd->base = base; + + if (zstd_read_seek_table(zstd)) { + zstd->reader.read = zstd_read_seekable; + zstd->reader.seek = zstd_seek; + } + else { + zstd->reader.read = zstd_read; + zstd->reader.seek = NULL; + + zstd->in_buf_max_size = ZSTD_DStreamInSize(); + zstd->in_buf.src = MEM_mallocN(zstd->in_buf_max_size, "zstd in buf"); + zstd->in_buf.size = zstd->in_buf_max_size; + /* This signals that the buffer has run out, + * which will make the read function refill it on the first call. */ + zstd->in_buf.pos = zstd->in_buf_max_size; + } + zstd->reader.close = zstd_close; + + return (FileReader *)zstd; +} diff --git a/source/blender/blenlib/intern/freetypefont.c b/source/blender/blenlib/intern/freetypefont.c index a8b50b66f5f..34de8fe7f6d 100644 --- a/source/blender/blenlib/intern/freetypefont.c +++ b/source/blender/blenlib/intern/freetypefont.c @@ -302,7 +302,7 @@ static VFontData *objfnt_to_ftvfontdata(PackedFile *pf) /* Get the name. */ if (face->family_name) { BLI_snprintf(vfd->name, sizeof(vfd->name), "%s %s", face->family_name, face->style_name); - BLI_utf8_invalid_strip(vfd->name, strlen(vfd->name)); + BLI_str_utf8_invalid_strip(vfd->name, strlen(vfd->name)); } /* Select a character map. */ @@ -369,36 +369,28 @@ static VFontData *objfnt_to_ftvfontdata(PackedFile *pf) return vfd; } -static int check_freetypefont(PackedFile *pf) +static bool check_freetypefont(PackedFile *pf) { - FT_Face face; - FT_GlyphSlot glyph; - FT_UInt glyph_index; - int success = 0; + FT_Face face = NULL; + FT_UInt glyph_index = 0; + bool success = false; err = FT_New_Memory_Face(library, pf->data, pf->size, 0, &face); if (err) { - success = 0; + return false; // XXX error("This is not a valid font"); } - else { - glyph_index = FT_Get_Char_Index(face, 'A'); + + FT_Get_First_Char(face, &glyph_index); + if (glyph_index) { err = FT_Load_Glyph(face, glyph_index, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP); - if (err) { - success = 0; - } - else { - glyph = face->glyph; - if (glyph->format == ft_glyph_format_outline) { - success = 1; - } - else { - // XXX error("Selected Font has no outline data"); - success = 0; - } + if (!err) { + success = (face->glyph->format == ft_glyph_format_outline); } } + FT_Done_Face(face); + return success; } @@ -413,7 +405,6 @@ static int check_freetypefont(PackedFile *pf) VFontData *BLI_vfontdata_from_freetypefont(PackedFile *pf) { VFontData *vfd = NULL; - int success = 0; /* init Freetype */ err = FT_Init_FreeType(&library); @@ -422,9 +413,7 @@ VFontData *BLI_vfontdata_from_freetypefont(PackedFile *pf) return NULL; } - success = check_freetypefont(pf); - - if (success) { + if (check_freetypefont(pf)) { vfd = objfnt_to_ftvfontdata(pf); } diff --git a/source/blender/blenlib/intern/index_mask.cc b/source/blender/blenlib/intern/index_mask.cc new file mode 100644 index 00000000000..cba985b8a44 --- /dev/null +++ b/source/blender/blenlib/intern/index_mask.cc @@ -0,0 +1,57 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_index_mask.hh" + +namespace blender { + +/** + * Create a sub-mask that is also shifted to the beginning. The shifting to the beginning allows + * code to work with smaller indices, which is more memory efficient. + * + * \return New index mask with the size of #slice. It is either empty or starts with 0. It might + * reference indices that have been appended to #r_new_indices. + * + * Example: + * this: [2, 3, 5, 7, 8, 9, 10] + * slice: ^--------^ + * output: [0, 2, 4, 5] + * + * All the indices in the sub-mask are shifted by 3 towards zero, so that the first index in the + * output is zero. + */ +IndexMask IndexMask::slice_and_offset(const IndexRange slice, Vector<int64_t> &r_new_indices) const +{ + const int slice_size = slice.size(); + if (slice_size == 0) { + return {}; + } + IndexMask sliced_mask{indices_.slice(slice)}; + if (sliced_mask.is_range()) { + return IndexMask(slice_size); + } + const int64_t offset = sliced_mask.indices().first(); + if (offset == 0) { + return sliced_mask; + } + r_new_indices.resize(slice_size); + for (const int i : IndexRange(slice_size)) { + r_new_indices[i] = sliced_mask[i] - offset; + } + return IndexMask(r_new_indices.as_span()); +} + +} // namespace blender diff --git a/source/blender/blenlib/intern/math_base_inline.c b/source/blender/blenlib/intern/math_base_inline.c index a80c495ecf3..49f9faf1704 100644 --- a/source/blender/blenlib/intern/math_base_inline.c +++ b/source/blender/blenlib/intern/math_base_inline.c @@ -363,6 +363,14 @@ MINLINE signed char round_db_to_char_clamp(double a){ #undef _round_clamp_fl_impl #undef _round_clamp_db_impl +/** + * Round to closest even number, halfway cases are rounded away from zero. + */ +MINLINE float round_to_even(float f) +{ + return roundf(f * 0.5f) * 2.0f; +} + /* integer division that rounds 0.5 up, particularly useful for color blending * with integers, to avoid gradual darkening when rounding down */ MINLINE int divide_round_i(int a, int b) @@ -648,6 +656,18 @@ MINLINE int compare_ff_relative(float a, float b, const float max_diff, const in return ((ua.i < 0) != (ub.i < 0)) ? 0 : (abs(ua.i - ub.i) <= max_ulps) ? 1 : 0; } +MINLINE bool compare_threshold_relative(const float value1, const float value2, const float thresh) +{ + const float abs_diff = fabsf(value1 - value2); + /* Avoid letting the threshold get too small just because the values happen to be close to zero. + */ + if (fabsf(value2) < 1) { + return abs_diff > thresh; + } + /* Using relative threshold in general. */ + return abs_diff > thresh * fabsf(value2); +} + MINLINE float signf(float f) { return (f < 0.0f) ? -1.0f : 1.0f; diff --git a/source/blender/blenlib/intern/noise.cc b/source/blender/blenlib/intern/noise.cc new file mode 100644 index 00000000000..c057c12e543 --- /dev/null +++ b/source/blender/blenlib/intern/noise.cc @@ -0,0 +1,693 @@ +/* + * Adapted from Open Shading Language with this license: + * + * Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al. + * All Rights Reserved. + * + * Modifications Copyright 2011, Blender Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Sony Pictures Imageworks nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <cmath> +#include <cstdint> + +#include "BLI_float2.hh" +#include "BLI_float3.hh" +#include "BLI_float4.hh" +#include "BLI_noise.hh" +#include "BLI_utildefines.h" + +namespace blender::noise { +/* ------------------------------ + * Jenkins Lookup3 Hash Functions + * ------------------------------ + * + * https://burtleburtle.net/bob/c/lookup3.c + * + */ + +BLI_INLINE uint32_t hash_bit_rotate(uint32_t x, uint32_t k) +{ + return (x << k) | (x >> (32 - k)); +} + +BLI_INLINE void hash_bit_mix(uint32_t &a, uint32_t &b, uint32_t &c) +{ + a -= c; + a ^= hash_bit_rotate(c, 4); + c += b; + b -= a; + b ^= hash_bit_rotate(a, 6); + a += c; + c -= b; + c ^= hash_bit_rotate(b, 8); + b += a; + a -= c; + a ^= hash_bit_rotate(c, 16); + c += b; + b -= a; + b ^= hash_bit_rotate(a, 19); + a += c; + c -= b; + c ^= hash_bit_rotate(b, 4); + b += a; +} + +BLI_INLINE void hash_bit_final(uint32_t &a, uint32_t &b, uint32_t &c) +{ + c ^= b; + c -= hash_bit_rotate(b, 14); + a ^= c; + a -= hash_bit_rotate(c, 11); + b ^= a; + b -= hash_bit_rotate(a, 25); + c ^= b; + c -= hash_bit_rotate(b, 16); + a ^= c; + a -= hash_bit_rotate(c, 4); + b ^= a; + b -= hash_bit_rotate(a, 14); + c ^= b; + c -= hash_bit_rotate(b, 24); +} + +BLI_INLINE uint32_t hash(uint32_t kx) +{ + uint32_t a, b, c; + a = b = c = 0xdeadbeef + (1 << 2) + 13; + + a += kx; + hash_bit_final(a, b, c); + + return c; +} + +BLI_INLINE uint32_t hash(uint32_t kx, uint32_t ky) +{ + uint32_t a, b, c; + a = b = c = 0xdeadbeef + (2 << 2) + 13; + + b += ky; + a += kx; + hash_bit_final(a, b, c); + + return c; +} + +BLI_INLINE uint32_t hash(uint32_t kx, uint32_t ky, uint32_t kz) +{ + uint32_t a, b, c; + a = b = c = 0xdeadbeef + (3 << 2) + 13; + + c += kz; + b += ky; + a += kx; + hash_bit_final(a, b, c); + + return c; +} + +BLI_INLINE uint32_t hash(uint32_t kx, uint32_t ky, uint32_t kz, uint32_t kw) +{ + uint32_t a, b, c; + a = b = c = 0xdeadbeef + (4 << 2) + 13; + + a += kx; + b += ky; + c += kz; + hash_bit_mix(a, b, c); + + a += kw; + hash_bit_final(a, b, c); + + return c; +} + +/* Hashing a number of uint32_t into a float in the range [0, 1]. */ + +BLI_INLINE float hash_to_float(uint32_t kx) +{ + return static_cast<float>(hash(kx)) / static_cast<float>(0xFFFFFFFFu); +} + +BLI_INLINE float hash_to_float(uint32_t kx, uint32_t ky) +{ + return static_cast<float>(hash(kx, ky)) / static_cast<float>(0xFFFFFFFFu); +} + +BLI_INLINE float hash_to_float(uint32_t kx, uint32_t ky, uint32_t kz) +{ + return static_cast<float>(hash(kx, ky, kz)) / static_cast<float>(0xFFFFFFFFu); +} + +BLI_INLINE float hash_to_float(uint32_t kx, uint32_t ky, uint32_t kz, uint32_t kw) +{ + return static_cast<float>(hash(kx, ky, kz, kw)) / static_cast<float>(0xFFFFFFFFu); +} + +/* Hashing a number of floats into a float in the range [0, 1]. */ + +BLI_INLINE uint32_t float_as_uint(float f) +{ + union { + uint32_t i; + float f; + } u; + u.f = f; + return u.i; +} + +BLI_INLINE float hash_to_float(float k) +{ + return hash_to_float(float_as_uint(k)); +} + +BLI_INLINE float hash_to_float(float2 k) +{ + return hash_to_float(float_as_uint(k.x), float_as_uint(k.y)); +} + +BLI_INLINE float hash_to_float(float3 k) +{ + return hash_to_float(float_as_uint(k.x), float_as_uint(k.y), float_as_uint(k.z)); +} + +BLI_INLINE float hash_to_float(float4 k) +{ + return hash_to_float( + float_as_uint(k.x), float_as_uint(k.y), float_as_uint(k.z), float_as_uint(k.w)); +} + +/* ------------ + * Perlin Noise + * ------------ + * + * Perlin, Ken. "Improving noise." Proceedings of the 29th annual conference on Computer graphics + * and interactive techniques. 2002. + * + * This implementation is functionally identical to the implementations in EEVEE, OSL, and SVM. So + * any changes should be applied in all relevant implementations. + */ + +/* Linear Interpolation. */ +BLI_INLINE float mix(float v0, float v1, float x) +{ + return (1 - x) * v0 + x * v1; +} + +/* Bilinear Interpolation: + * + * v2 v3 + * @ + + + + @ y + * + + ^ + * + + | + * + + | + * @ + + + + @ @------> x + * v0 v1 + * + */ +BLI_INLINE float mix(float v0, float v1, float v2, float v3, float x, float y) +{ + float x1 = 1.0 - x; + return (1.0 - y) * (v0 * x1 + v1 * x) + y * (v2 * x1 + v3 * x); +} + +/* Trilinear Interpolation: + * + * v6 v7 + * @ + + + + + + @ + * +\ +\ + * + \ + \ + * + \ + \ + * + \ v4 + \ v5 + * + @ + + + +++ + @ z + * + + + + y ^ + * v2 @ + +++ + + + @ v3 + \ | + * \ + \ + \ | + * \ + \ + \| + * \ + \ + +---------> x + * \+ \+ + * @ + + + + + + @ + * v0 v1 + */ +BLI_INLINE float mix(float v0, + float v1, + float v2, + float v3, + float v4, + float v5, + float v6, + float v7, + float x, + float y, + float z) +{ + float x1 = 1.0 - x; + float y1 = 1.0 - y; + float z1 = 1.0 - z; + return z1 * (y1 * (v0 * x1 + v1 * x) + y * (v2 * x1 + v3 * x)) + + z * (y1 * (v4 * x1 + v5 * x) + y * (v6 * x1 + v7 * x)); +} + +/* Quadrilinear Interpolation. */ +BLI_INLINE float mix(float v0, + float v1, + float v2, + float v3, + float v4, + float v5, + float v6, + float v7, + float v8, + float v9, + float v10, + float v11, + float v12, + float v13, + float v14, + float v15, + float x, + float y, + float z, + float w) +{ + return mix(mix(v0, v1, v2, v3, v4, v5, v6, v7, x, y, z), + mix(v8, v9, v10, v11, v12, v13, v14, v15, x, y, z), + w); +} + +BLI_INLINE float fade(float t) +{ + return t * t * t * (t * (t * 6.0 - 15.0) + 10.0); +} + +BLI_INLINE float negate_if(float value, uint32_t condition) +{ + return (condition != 0u) ? -value : value; +} + +BLI_INLINE float noise_grad(uint32_t hash, float x) +{ + uint32_t h = hash & 15u; + float g = 1u + (h & 7u); + return negate_if(g, h & 8u) * x; +} + +BLI_INLINE float noise_grad(uint32_t hash, float x, float y) +{ + uint32_t h = hash & 7u; + float u = h < 4u ? x : y; + float v = 2.0 * (h < 4u ? y : x); + return negate_if(u, h & 1u) + negate_if(v, h & 2u); +} + +BLI_INLINE float noise_grad(uint32_t hash, float x, float y, float z) +{ + uint32_t h = hash & 15u; + float u = h < 8u ? x : y; + float vt = ((h == 12u) || (h == 14u)) ? x : z; + float v = h < 4u ? y : vt; + return negate_if(u, h & 1u) + negate_if(v, h & 2u); +} + +BLI_INLINE float noise_grad(uint32_t hash, float x, float y, float z, float w) +{ + uint32_t h = hash & 31u; + float u = h < 24u ? x : y; + float v = h < 16u ? y : z; + float s = h < 8u ? z : w; + return negate_if(u, h & 1u) + negate_if(v, h & 2u) + negate_if(s, h & 4u); +} + +BLI_INLINE float floor_fraction(float x, int &i) +{ + i = (int)x - ((x < 0) ? 1 : 0); + return x - i; +} + +BLI_INLINE float perlin_noise(float position) +{ + int X; + + float fx = floor_fraction(position, X); + + float u = fade(fx); + + float r = mix(noise_grad(hash(X), fx), noise_grad(hash(X + 1), fx - 1.0), u); + + return r; +} + +BLI_INLINE float perlin_noise(float2 position) +{ + int X, Y; + + float fx = floor_fraction(position.x, X); + float fy = floor_fraction(position.y, Y); + + float u = fade(fx); + float v = fade(fy); + + float r = mix(noise_grad(hash(X, Y), fx, fy), + noise_grad(hash(X + 1, Y), fx - 1.0, fy), + noise_grad(hash(X, Y + 1), fx, fy - 1.0), + noise_grad(hash(X + 1, Y + 1), fx - 1.0, fy - 1.0), + u, + v); + + return r; +} + +BLI_INLINE float perlin_noise(float3 position) +{ + int X, Y, Z; + + float fx = floor_fraction(position.x, X); + float fy = floor_fraction(position.y, Y); + float fz = floor_fraction(position.z, Z); + + float u = fade(fx); + float v = fade(fy); + float w = fade(fz); + + float r = mix(noise_grad(hash(X, Y, Z), fx, fy, fz), + noise_grad(hash(X + 1, Y, Z), fx - 1, fy, fz), + noise_grad(hash(X, Y + 1, Z), fx, fy - 1, fz), + noise_grad(hash(X + 1, Y + 1, Z), fx - 1, fy - 1, fz), + noise_grad(hash(X, Y, Z + 1), fx, fy, fz - 1), + noise_grad(hash(X + 1, Y, Z + 1), fx - 1, fy, fz - 1), + noise_grad(hash(X, Y + 1, Z + 1), fx, fy - 1, fz - 1), + noise_grad(hash(X + 1, Y + 1, Z + 1), fx - 1, fy - 1, fz - 1), + u, + v, + w); + + return r; +} + +BLI_INLINE float perlin_noise(float4 position) +{ + int X, Y, Z, W; + + float fx = floor_fraction(position.x, X); + float fy = floor_fraction(position.y, Y); + float fz = floor_fraction(position.z, Z); + float fw = floor_fraction(position.w, W); + + float u = fade(fx); + float v = fade(fy); + float t = fade(fz); + float s = fade(fw); + + float r = mix( + noise_grad(hash(X, Y, Z, W), fx, fy, fz, fw), + noise_grad(hash(X + 1, Y, Z, W), fx - 1.0, fy, fz, fw), + noise_grad(hash(X, Y + 1, Z, W), fx, fy - 1.0, fz, fw), + noise_grad(hash(X + 1, Y + 1, Z, W), fx - 1.0, fy - 1.0, fz, fw), + noise_grad(hash(X, Y, Z + 1, W), fx, fy, fz - 1.0, fw), + noise_grad(hash(X + 1, Y, Z + 1, W), fx - 1.0, fy, fz - 1.0, fw), + noise_grad(hash(X, Y + 1, Z + 1, W), fx, fy - 1.0, fz - 1.0, fw), + noise_grad(hash(X + 1, Y + 1, Z + 1, W), fx - 1.0, fy - 1.0, fz - 1.0, fw), + noise_grad(hash(X, Y, Z, W + 1), fx, fy, fz, fw - 1.0), + noise_grad(hash(X + 1, Y, Z, W + 1), fx - 1.0, fy, fz, fw - 1.0), + noise_grad(hash(X, Y + 1, Z, W + 1), fx, fy - 1.0, fz, fw - 1.0), + noise_grad(hash(X + 1, Y + 1, Z, W + 1), fx - 1.0, fy - 1.0, fz, fw - 1.0), + noise_grad(hash(X, Y, Z + 1, W + 1), fx, fy, fz - 1.0, fw - 1.0), + noise_grad(hash(X + 1, Y, Z + 1, W + 1), fx - 1.0, fy, fz - 1.0, fw - 1.0), + noise_grad(hash(X, Y + 1, Z + 1, W + 1), fx, fy - 1.0, fz - 1.0, fw - 1.0), + noise_grad(hash(X + 1, Y + 1, Z + 1, W + 1), fx - 1.0, fy - 1.0, fz - 1.0, fw - 1.0), + u, + v, + t, + s); + + return r; +} + +/* Signed versions of perlin noise in the range [-1, 1]. The scale values were computed + * experimentally by the OSL developers to remap the noise output to the correct range. */ + +float perlin_signed(float position) +{ + return perlin_noise(position) * 0.2500f; +} + +float perlin_signed(float2 position) +{ + return perlin_noise(position) * 0.6616f; +} + +float perlin_signed(float3 position) +{ + return perlin_noise(position) * 0.9820f; +} + +float perlin_signed(float4 position) +{ + return perlin_noise(position) * 0.8344f; +} + +/* Positive versions of perlin noise in the range [0, 1]. */ + +float perlin(float position) +{ + return perlin_signed(position) / 2.0f + 0.5f; +} + +float perlin(float2 position) +{ + return perlin_signed(position) / 2.0f + 0.5f; +} + +float perlin(float3 position) +{ + return perlin_signed(position) / 2.0f + 0.5f; +} + +float perlin(float4 position) +{ + return perlin_signed(position) / 2.0f + 0.5f; +} + +/* Positive fractal perlin noise. */ + +template<typename T> float perlin_fractal_template(T position, float octaves, float roughness) +{ + float fscale = 1.0f; + float amp = 1.0f; + float maxamp = 0.0f; + float sum = 0.0f; + octaves = CLAMPIS(octaves, 0.0f, 16.0f); + int n = static_cast<int>(octaves); + for (int i = 0; i <= n; i++) { + float t = perlin(fscale * position); + sum += t * amp; + maxamp += amp; + amp *= CLAMPIS(roughness, 0.0f, 1.0f); + fscale *= 2.0f; + } + float rmd = octaves - std::floor(octaves); + if (rmd == 0.0f) { + return sum / maxamp; + } + + float t = perlin(fscale * position); + float sum2 = sum + t * amp; + sum /= maxamp; + sum2 /= maxamp + amp; + return (1.0f - rmd) * sum + rmd * sum2; +} + +float perlin_fractal(float position, float octaves, float roughness) +{ + return perlin_fractal_template(position, octaves, roughness); +} + +float perlin_fractal(float2 position, float octaves, float roughness) +{ + return perlin_fractal_template(position, octaves, roughness); +} + +float perlin_fractal(float3 position, float octaves, float roughness) +{ + return perlin_fractal_template(position, octaves, roughness); +} + +float perlin_fractal(float4 position, float octaves, float roughness) +{ + return perlin_fractal_template(position, octaves, roughness); +} + +/* The following offset functions generate random offsets to be added to + * positions to act as a seed since the noise functions don't have seed values. + * The offset's components are in the range [100, 200], not too high to cause + * bad precision and not too small to be noticeable. We use float seed because + * OSL only support float hashes and we need to maintain compatibility with it. + */ + +BLI_INLINE float random_float_offset(float seed) +{ + return 100.0f + hash_to_float(seed) * 100.0f; +} + +BLI_INLINE float2 random_float2_offset(float seed) +{ + return float2(100.0f + hash_to_float(float2(seed, 0.0f)) * 100.0f, + 100.0f + hash_to_float(float2(seed, 1.0f)) * 100.0f); +} + +BLI_INLINE float3 random_float3_offset(float seed) +{ + return float3(100.0f + hash_to_float(float2(seed, 0.0f)) * 100.0f, + 100.0f + hash_to_float(float2(seed, 1.0f)) * 100.0f, + 100.0f + hash_to_float(float2(seed, 2.0f)) * 100.0f); +} + +BLI_INLINE float4 random_float4_offset(float seed) +{ + return float4(100.0f + hash_to_float(float2(seed, 0.0f)) * 100.0f, + 100.0f + hash_to_float(float2(seed, 1.0f)) * 100.0f, + 100.0f + hash_to_float(float2(seed, 2.0f)) * 100.0f, + 100.0f + hash_to_float(float2(seed, 3.0f)) * 100.0f); +} + +/* Perlin noises to be added to the position to distort other noises. */ + +BLI_INLINE float perlin_distortion(float position, float strength) +{ + return perlin_signed(position + random_float_offset(0.0)) * strength; +} + +BLI_INLINE float2 perlin_distortion(float2 position, float strength) +{ + return float2(perlin_signed(position + random_float2_offset(0.0f)) * strength, + perlin_signed(position + random_float2_offset(1.0f)) * strength); +} + +BLI_INLINE float3 perlin_distortion(float3 position, float strength) +{ + return float3(perlin_signed(position + random_float3_offset(0.0f)) * strength, + perlin_signed(position + random_float3_offset(1.0f)) * strength, + perlin_signed(position + random_float3_offset(2.0f)) * strength); +} + +BLI_INLINE float4 perlin_distortion(float4 position, float strength) +{ + return float4(perlin_signed(position + random_float4_offset(0.0f)) * strength, + perlin_signed(position + random_float4_offset(1.0f)) * strength, + perlin_signed(position + random_float4_offset(2.0f)) * strength, + perlin_signed(position + random_float4_offset(3.0f)) * strength); +} + +/* Positive distorted fractal perlin noise. */ + +float perlin_fractal_distorted(float position, float octaves, float roughness, float distortion) +{ + position += perlin_distortion(position, distortion); + return perlin_fractal(position, octaves, roughness); +} + +float perlin_fractal_distorted(float2 position, float octaves, float roughness, float distortion) +{ + position += perlin_distortion(position, distortion); + return perlin_fractal(position, octaves, roughness); +} + +float perlin_fractal_distorted(float3 position, float octaves, float roughness, float distortion) +{ + position += perlin_distortion(position, distortion); + return perlin_fractal(position, octaves, roughness); +} + +float perlin_fractal_distorted(float4 position, float octaves, float roughness, float distortion) +{ + position += perlin_distortion(position, distortion); + return perlin_fractal(position, octaves, roughness); +} + +/* Positive distorted fractal perlin noise that outputs a float3. The arbitrary seeds are for + * compatibility with shading functions. */ + +float3 perlin_float3_fractal_distorted(float position, + float octaves, + float roughness, + float distortion) +{ + position += perlin_distortion(position, distortion); + return float3(perlin_fractal(position, octaves, roughness), + perlin_fractal(position + random_float_offset(1.0f), octaves, roughness), + perlin_fractal(position + random_float_offset(2.0f), octaves, roughness)); +} + +float3 perlin_float3_fractal_distorted(float2 position, + float octaves, + float roughness, + float distortion) +{ + position += perlin_distortion(position, distortion); + return float3(perlin_fractal(position, octaves, roughness), + perlin_fractal(position + random_float2_offset(2.0f), octaves, roughness), + perlin_fractal(position + random_float2_offset(3.0f), octaves, roughness)); +} + +float3 perlin_float3_fractal_distorted(float3 position, + float octaves, + float roughness, + float distortion) +{ + position += perlin_distortion(position, distortion); + return float3(perlin_fractal(position, octaves, roughness), + perlin_fractal(position + random_float3_offset(3.0f), octaves, roughness), + perlin_fractal(position + random_float3_offset(4.0f), octaves, roughness)); +} + +float3 perlin_float3_fractal_distorted(float4 position, + float octaves, + float roughness, + float distortion) +{ + position += perlin_distortion(position, distortion); + return float3(perlin_fractal(position, octaves, roughness), + perlin_fractal(position + random_float4_offset(4.0f), octaves, roughness), + perlin_fractal(position + random_float4_offset(5.0f), octaves, roughness)); +} + +} // namespace blender::noise diff --git a/source/blender/blenlib/intern/string.c b/source/blender/blenlib/intern/string.c index 5541d75bc73..0ea784c95b0 100644 --- a/source/blender/blenlib/intern/string.c +++ b/source/blender/blenlib/intern/string.c @@ -360,6 +360,27 @@ size_t BLI_str_escape(char *__restrict dst, const char *__restrict src, const si return len; } +BLI_INLINE bool str_unescape_pair(char c_next, char *r_out) +{ +#define CASE_PAIR(value_src, value_dst) \ + case value_src: { \ + *r_out = value_dst; \ + return true; \ + } + switch (c_next) { + CASE_PAIR('"', '"'); /* Quote. */ + CASE_PAIR('\\', '\\'); /* Backslash. */ + CASE_PAIR('t', '\t'); /* Tab. */ + CASE_PAIR('n', '\n'); /* Newline. */ + CASE_PAIR('r', '\r'); /* Carriage return. */ + CASE_PAIR('a', '\a'); /* Bell. */ + CASE_PAIR('b', '\b'); /* Backspace. */ + CASE_PAIR('f', '\f'); /* Form-feed. */ + } +#undef CASE_PAIR + return false; +} + /** * This roughly matches C and Python's string escaping with double quotes - `"`. * @@ -368,31 +389,53 @@ size_t BLI_str_escape(char *__restrict dst, const char *__restrict src, const si * * \param dst: The destination string, at least the size of `strlen(src) + 1`. * \param src: The escaped source string. - * \param dst_maxncpy: The maximum number of bytes allowable to copy. + * \param src_maxncpy: The maximum number of bytes allowable to copy from `src`. + * \param dst_maxncpy: The maximum number of bytes allowable to copy into `dst`. + * \param r_is_complete: Set to true when + */ +size_t BLI_str_unescape_ex(char *__restrict dst, + const char *__restrict src, + const size_t src_maxncpy, + /* Additional arguments to #BLI_str_unescape */ + const size_t dst_maxncpy, + bool *r_is_complete) +{ + size_t len = 0; + bool is_complete = true; + for (const char *src_end = src + src_maxncpy; (src < src_end) && *src; src++) { + if (UNLIKELY(len == dst_maxncpy)) { + is_complete = false; + break; + } + char c = *src; + if (UNLIKELY(c == '\\') && (str_unescape_pair(*(src + 1), &c))) { + src++; + } + dst[len++] = c; + } + dst[len] = 0; + *r_is_complete = is_complete; + return len; +} + +/** + * See #BLI_str_unescape_ex doc-string. + * + * This function makes the assumption that `dst` always has + * at least `src_maxncpy` bytes available. * - * \note This is used for parsing animation paths in blend files. + * Use #BLI_str_unescape_ex if `dst` has a smaller fixed size. + * + * \note This is used for parsing animation paths in blend files (runs often). */ size_t BLI_str_unescape(char *__restrict dst, const char *__restrict src, const size_t src_maxncpy) { size_t len = 0; - for (size_t i = 0; i < src_maxncpy && (*src != '\0'); i++, src++) { + for (const char *src_end = src + src_maxncpy; (src < src_end) && *src; src++) { char c = *src; - if (c == '\\') { - char c_next = *(src + 1); - if (((c_next == '"') && ((void)(c = '"'), true)) || /* Quote. */ - ((c_next == '\\') && ((void)(c = '\\'), true)) || /* Backslash. */ - ((c_next == 't') && ((void)(c = '\t'), true)) || /* Tab. */ - ((c_next == 'n') && ((void)(c = '\n'), true)) || /* Newline. */ - ((c_next == 'r') && ((void)(c = '\r'), true)) || /* Carriage return. */ - ((c_next == 'a') && ((void)(c = '\a'), true)) || /* Bell. */ - ((c_next == 'b') && ((void)(c = '\b'), true)) || /* Backspace. */ - ((c_next == 'f') && ((void)(c = '\f'), true))) /* Form-feed. */ - { - i++; - src++; - } + if (UNLIKELY(c == '\\') && (str_unescape_pair(*(src + 1), &c))) { + src++; } - dst[len++] = c; } dst[len] = 0; @@ -420,7 +463,58 @@ const char *BLI_str_escape_find_quote(const char *str) } /** - * Makes a copy of the text within the "" that appear after some text `blahblah`. + * Return the range of the quoted string (excluding quotes) `str` after `prefix`. + * + * A version of #BLI_str_quoted_substrN that calculates the range + * instead of un-escaping and allocating the result. + * + * \param str: String potentially including `prefix`. + * \param prefix: Quoted string prefix. + * \param r_start: The start of the quoted string (after the first quote). + * \param r_end: The end of the quoted string (before the last quote). + * \return True when a quoted string range could be found after `prefix`. + */ +bool BLI_str_quoted_substr_range(const char *__restrict str, + const char *__restrict prefix, + int *__restrict r_start, + int *__restrict r_end) +{ + const char *str_start = strstr(str, prefix); + if (str_start == NULL) { + return false; + } + const size_t prefix_len = strlen(prefix); + if (UNLIKELY(prefix_len == 0)) { + BLI_assert_msg(0, + "Zero length prefix passed in, " + "caller must prevent this from happening!"); + return false; + } + BLI_assert_msg(prefix[prefix_len - 1] != '"', + "Prefix includes trailing quote, " + "caller must prevent this from happening!"); + + str_start += prefix_len; + if (UNLIKELY(*str_start != '\"')) { + return false; + } + str_start += 1; + const char *str_end = BLI_str_escape_find_quote(str_start); + if (UNLIKELY(str_end == NULL)) { + return false; + } + + *r_start = (int)(str_start - str); + *r_end = (int)(str_end - str); + return true; +} + +/* NOTE(@campbellbarton): in principal it should be possible to access a quoted string + * with an arbitrary size, currently all callers for this functionality + * happened to use a fixed size buffer, so only #BLI_str_quoted_substr is needed. */ +#if 0 +/** + * Makes a copy of the text within the "" that appear after the contents of \a prefix. * i.e. for string `pose["apples"]` with prefix `pose[`, it will return `apples`. * * \param str: is the entire string to chop. @@ -431,27 +525,49 @@ const char *BLI_str_escape_find_quote(const char *str) */ char *BLI_str_quoted_substrN(const char *__restrict str, const char *__restrict prefix) { - const char *start_match, *end_match; - - /* get the starting point (i.e. where prefix starts, and add prefix_len+1 - * to it to get be after the first " */ - start_match = strstr(str, prefix); - if (start_match) { - const size_t prefix_len = strlen(prefix); - start_match += prefix_len + 1; - /* get the end point (i.e. where the next occurrence of " is after the starting point) */ - end_match = BLI_str_escape_find_quote(start_match); - if (end_match) { - const size_t escaped_len = (size_t)(end_match - start_match); - char *result = MEM_mallocN(sizeof(char) * (escaped_len + 1), __func__); - const size_t unescaped_len = BLI_str_unescape(result, start_match, escaped_len); - if (unescaped_len != escaped_len) { - result = MEM_reallocN(result, sizeof(char) * (unescaped_len + 1)); - } - return result; - } + int start_match_ofs, end_match_ofs; + if (!BLI_str_quoted_substr_range(str, prefix, &start_match_ofs, &end_match_ofs)) { + return NULL; + } + const size_t escaped_len = (size_t)(end_match_ofs - start_match_ofs); + char *result = MEM_mallocN(sizeof(char) * (escaped_len + 1), __func__); + const size_t unescaped_len = BLI_str_unescape(result, str + start_match_ofs, escaped_len); + if (unescaped_len != escaped_len) { + result = MEM_reallocN(result, sizeof(char) * (unescaped_len + 1)); + } + return result; +} +#endif + +/** + * Fills \a result with text within "" that appear after some the contents of \a prefix. + * i.e. for string `pose["apples"]` with prefix `pose[`, it will return `apples`. + * + * \param str: is the entire string to chop. + * \param prefix: is the part of the string to step over. + * \param result: The buffer to fill. + * \param result_maxlen: The maximum size of the buffer (including nil terminator). + * \return True if the prefix was found and the entire quoted string was copied into result. + * + * Assume that the strings returned must be freed afterwards, + * and that the inputs will contain data we want. + */ +bool BLI_str_quoted_substr(const char *__restrict str, + const char *__restrict prefix, + char *result, + size_t result_maxlen) +{ + int start_match_ofs, end_match_ofs; + if (!BLI_str_quoted_substr_range(str, prefix, &start_match_ofs, &end_match_ofs)) { + return false; + } + const size_t escaped_len = (size_t)(end_match_ofs - start_match_ofs); + bool is_complete; + BLI_str_unescape_ex(result, str + start_match_ofs, escaped_len, result_maxlen, &is_complete); + if (is_complete == false) { + *result = '\0'; } - return NULL; + return is_complete; } /** diff --git a/source/blender/blenlib/intern/string_cursor_utf8.c b/source/blender/blenlib/intern/string_cursor_utf8.c index 90fde02b11f..eb49572f06c 100644 --- a/source/blender/blenlib/intern/string_cursor_utf8.c +++ b/source/blender/blenlib/intern/string_cursor_utf8.c @@ -101,11 +101,14 @@ static eStrCursorDelimType cursor_delim_type_unicode(const uint uch) return STRCUR_DELIM_ALPHANUMERIC; /* Not quite true, but ok for now */ } -static eStrCursorDelimType cursor_delim_type_utf8(const char *ch_utf8) +static eStrCursorDelimType cursor_delim_type_utf8(const char *ch_utf8, + const size_t ch_utf8_len, + const int pos) { /* for full unicode support we really need to have large lookup tables to figure * out what's what in every possible char set - and python, glib both have these. */ - uint uch = BLI_str_utf8_as_unicode(ch_utf8); + size_t index = (size_t)pos; + uint uch = BLI_str_utf8_as_unicode_step_or_error(ch_utf8, ch_utf8_len, &index); return cursor_delim_type_unicode(uch); } @@ -114,7 +117,7 @@ bool BLI_str_cursor_step_next_utf8(const char *str, size_t maxlen, int *pos) const char *str_end = str + (maxlen + 1); const char *str_pos = str + (*pos); const char *str_next = BLI_str_find_next_char_utf8(str_pos, str_end); - if (str_next) { + if (str_next != str_end) { (*pos) += (str_next - str_pos); if ((*pos) > (int)maxlen) { (*pos) = (int)maxlen; @@ -129,11 +132,9 @@ bool BLI_str_cursor_step_prev_utf8(const char *str, size_t UNUSED(maxlen), int * { if ((*pos) > 0) { const char *str_pos = str + (*pos); - const char *str_prev = BLI_str_find_prev_char_utf8(str, str_pos); - if (str_prev) { - (*pos) -= (str_pos - str_prev); - return true; - } + const char *str_prev = BLI_str_find_prev_char_utf8(str_pos, str); + (*pos) -= (str_pos - str_prev); + return true; } return false; @@ -157,14 +158,19 @@ void BLI_str_cursor_step_utf8(const char *str, } if (jump != STRCUR_JUMP_NONE) { - const eStrCursorDelimType delim_type = (*pos) < maxlen ? cursor_delim_type_utf8(&str[*pos]) : - STRCUR_DELIM_NONE; + const eStrCursorDelimType delim_type = (*pos) < maxlen ? + cursor_delim_type_utf8(str, maxlen, *pos) : + STRCUR_DELIM_NONE; /* jump between special characters (/,\,_,-, etc.), * look at function cursor_delim_type() for complete * list of special character, ctr -> */ while ((*pos) < maxlen) { if (BLI_str_cursor_step_next_utf8(str, maxlen, pos)) { - if ((jump != STRCUR_JUMP_ALL) && (delim_type != cursor_delim_type_utf8(&str[*pos]))) { + if (*pos == maxlen) { + break; + } + if ((jump != STRCUR_JUMP_ALL) && + (delim_type != cursor_delim_type_utf8(str, maxlen, *pos))) { break; } } @@ -184,7 +190,7 @@ void BLI_str_cursor_step_utf8(const char *str, if (jump != STRCUR_JUMP_NONE) { const eStrCursorDelimType delim_type = (*pos) > 0 ? - cursor_delim_type_utf8(&str[(*pos) - 1]) : + cursor_delim_type_utf8(str, maxlen, *pos - 1) : STRCUR_DELIM_NONE; /* jump between special characters (/,\,_,-, etc.), * look at function cursor_delim_type() for complete @@ -192,7 +198,8 @@ void BLI_str_cursor_step_utf8(const char *str, while ((*pos) > 0) { const int pos_prev = *pos; if (BLI_str_cursor_step_prev_utf8(str, maxlen, pos)) { - if ((jump != STRCUR_JUMP_ALL) && (delim_type != cursor_delim_type_utf8(&str[*pos]))) { + if ((jump != STRCUR_JUMP_ALL) && + (delim_type != cursor_delim_type_utf8(str, maxlen, (size_t)*pos))) { /* left only: compensate for index/change in direction */ if ((pos_orig - (*pos)) >= 1) { *pos = pos_prev; diff --git a/source/blender/blenlib/intern/string_search.cc b/source/blender/blenlib/intern/string_search.cc index 25a13674932..a466c124073 100644 --- a/source/blender/blenlib/intern/string_search.cc +++ b/source/blender/blenlib/intern/string_search.cc @@ -71,12 +71,12 @@ int damerau_levenshtein_distance(StringRef a, StringRef b) for (const int i : IndexRange(size_a)) { v2[0] = (i + 1) * deletion_cost; - const uint32_t unicode_a = BLI_str_utf8_as_unicode_and_size(a.data() + offset_a, &offset_a); + const uint32_t unicode_a = BLI_str_utf8_as_unicode_step(a.data(), a.size(), &offset_a); uint32_t prev_unicode_b; size_t offset_b = 0; for (const int j : IndexRange(size_b)) { - const uint32_t unicode_b = BLI_str_utf8_as_unicode_and_size(b.data() + offset_b, &offset_b); + const uint32_t unicode_b = BLI_str_utf8_as_unicode_step(b.data(), b.size(), &offset_b); /* Check how costly the different operations would be and pick the cheapest - the one with * minimal cost. */ @@ -202,8 +202,8 @@ static bool match_word_initials(StringRef query, int first_found_word_index = -1; while (query_index < query.size()) { - const uint query_unicode = BLI_str_utf8_as_unicode_and_size(query.data() + query_index, - &query_index); + const uint query_unicode = BLI_str_utf8_as_unicode_step( + query.data(), query.size(), &query_index); while (true) { /* We are at the end of words, no complete match has been found yet. */ if (word_index >= words.size()) { @@ -226,8 +226,8 @@ static bool match_word_initials(StringRef query, StringRef word = words[word_index]; /* Try to match the current character with the current word. */ if (static_cast<int>(char_index) < word.size()) { - const uint32_t char_unicode = BLI_str_utf8_as_unicode_and_size(word.data() + char_index, - &char_index); + const uint32_t char_unicode = BLI_str_utf8_as_unicode_step( + word.data(), word.size(), &char_index); if (query_unicode == char_unicode) { r_word_is_matched[word_index] = true; if (first_found_word_index == -1) { @@ -368,8 +368,9 @@ void extract_normalized_words(StringRef str, size_t word_start = 0; size_t offset = 0; while (offset < str_size_in_bytes) { - size_t size = 0; - uint32_t unicode = BLI_str_utf8_as_unicode_and_size(str.data() + offset, &size); + size_t size = offset; + uint32_t unicode = BLI_str_utf8_as_unicode_step(str.data(), str.size(), &size); + size -= offset; if (is_separator(unicode)) { if (is_in_word) { r_words.append( diff --git a/source/blender/blenlib/intern/string_utf8.c b/source/blender/blenlib/intern/string_utf8.c index 19ff8764259..b9ea538ff24 100644 --- a/source/blender/blenlib/intern/string_utf8.c +++ b/source/blender/blenlib/intern/string_utf8.c @@ -70,7 +70,7 @@ static const size_t utf8_skip_data[256] = { * * \return the offset of the first invalid byte. */ -ptrdiff_t BLI_utf8_invalid_byte(const char *str, size_t length) +ptrdiff_t BLI_str_utf8_invalid_byte(const char *str, size_t length) { const unsigned char *p, *perr, *pend = (const unsigned char *)str + length; unsigned char c; @@ -200,14 +200,14 @@ utf8_error: * * \return number of stripped bytes. */ -int BLI_utf8_invalid_strip(char *str, size_t length) +int BLI_str_utf8_invalid_strip(char *str, size_t length) { ptrdiff_t bad_char; int tot = 0; BLI_assert(str[length] == '\0'); - while ((bad_char = BLI_utf8_invalid_byte(str, length)) != -1) { + while ((bad_char = BLI_str_utf8_invalid_byte(str, length)) != -1) { str += bad_char; length -= (size_t)(bad_char + 1); @@ -296,36 +296,19 @@ size_t BLI_strncpy_wchar_as_utf8(char *__restrict dst, const wchar_t *__restrict src, const size_t maxncpy) { - const size_t maxlen = maxncpy - 1; - /* 6 is max utf8 length of an unicode char. */ - const int64_t maxlen_secured = (int64_t)maxlen - 6; - size_t len = 0; - BLI_assert(maxncpy != 0); - + size_t len = 0; #ifdef DEBUG_STRSIZE memset(dst, 0xff, sizeof(*dst) * maxncpy); #endif - - while (*src && len <= maxlen_secured) { - len += BLI_str_utf8_from_unicode((uint)*src++, dst + len); - } - - /* We have to be more careful for the last six bytes, - * to avoid buffer overflow in case utf8-encoded char would be too long for our dst buffer. */ - while (*src) { - char t[6]; - size_t l = BLI_str_utf8_from_unicode((uint)*src++, t); - BLI_assert(l <= 6); - if (len + l > maxlen) { - break; - } - memcpy(dst + len, t, l); - len += l; + while (*src && len < maxncpy) { + len += BLI_str_utf8_from_unicode((uint)*src++, dst + len, maxncpy - len); } - dst[len] = '\0'; - + /* Return the correct length when part of the final byte did not fit into the string. */ + while ((len > 0) && UNLIKELY(dst[len - 1] == '\0')) { + len--; + } return len; } @@ -335,7 +318,7 @@ size_t BLI_wstrlen_utf8(const wchar_t *src) size_t len = 0; while (*src) { - len += BLI_str_utf8_from_unicode((uint)*src++, NULL); + len += BLI_str_utf8_from_unicode_len((uint)*src++); } return len; @@ -395,7 +378,9 @@ size_t BLI_strncpy_wchar_from_utf8(wchar_t *__restrict dst_w, const size_t maxncpy) { #ifdef WIN32 - return conv_utf_8_to_16(src_c, dst_w, maxncpy); + conv_utf_8_to_16(src_c, dst_w, maxncpy); + /* NOTE: it would be more efficient to calculate the length as part of #conv_utf_8_to_16. */ + return wcslen(dst_w); #else return BLI_str_utf8_as_utf32((char32_t *)dst_w, src_c, maxncpy); #endif @@ -546,139 +531,140 @@ uint BLI_str_utf8_as_unicode(const char *p) return result; } -/* variant that increments the length */ -uint BLI_str_utf8_as_unicode_and_size(const char *__restrict p, size_t *__restrict index) +/** + * UTF8 decoding that steps over the index (unless an error is encountered). + * + * \param p: The text to step over. + * \param p_len: The length of `p`. + * \param index: Index of `p` to step over. + * \return the code-point or #BLI_UTF8_ERR if there is a decoding error. + * + * \note The behavior for clipped text (where `p_len` limits decoding trailing bytes) + * must have the same behavior is encountering a nil byte, + * so functions that only use the first part of a string has matching behavior to functions + * that null terminate the text. + */ +uint BLI_str_utf8_as_unicode_step_or_error(const char *__restrict p, + const size_t p_len, + size_t *__restrict index) { int i, len; uint mask = 0; uint result; - const unsigned char c = (unsigned char)*p; + const unsigned char c = (unsigned char)*(p += *index); + + BLI_assert(*index < p_len); + BLI_assert(c != '\0'); UTF8_COMPUTE(c, mask, len, -1); - if (UNLIKELY(len == -1)) { + if (UNLIKELY(len == -1) || (*index + (size_t)len > p_len)) { return BLI_UTF8_ERR; } UTF8_GET(result, p, i, mask, len, BLI_UTF8_ERR); + if (UNLIKELY(result == BLI_UTF8_ERR)) { + return BLI_UTF8_ERR; + } *index += (size_t)len; + BLI_assert(*index <= p_len); return result; } -uint BLI_str_utf8_as_unicode_and_size_safe(const char *__restrict p, size_t *__restrict index) +/** + * UTF8 decoding that steps over the index (unless an error is encountered). + * + * \param p: The text to step over. + * \param p_len: The length of `p`. + * \param index: Index of `p` to step over. + * \return the code-point `(p + *index)` if there is a decoding error. + * + * \note Falls back to `LATIN1` for text drawing. + */ +uint BLI_str_utf8_as_unicode_step(const char *__restrict p, + const size_t p_len, + size_t *__restrict index) { - int i, len; - uint mask = 0; - uint result; - const unsigned char c = (unsigned char)*p; - - UTF8_COMPUTE(c, mask, len, -1); - if (UNLIKELY(len == -1)) { + uint result = BLI_str_utf8_as_unicode_step_or_error(p, p_len, index); + if (UNLIKELY(result == BLI_UTF8_ERR)) { + result = (uint)p[*index]; *index += 1; - return c; } - UTF8_GET(result, p, i, mask, len, BLI_UTF8_ERR); - *index += (size_t)len; + BLI_assert(*index <= p_len); return result; } -/** - * Another variant that steps over the index. - * \note currently this also falls back to latin1 for text drawing. - */ -uint BLI_str_utf8_as_unicode_step(const char *__restrict p, size_t *__restrict index) -{ - int i, len; - uint mask = 0; - uint result; - unsigned char c; +/* was g_unichar_to_utf8 */ - p += *index; - c = (unsigned char)*p; +#define UTF8_VARS_FROM_CHAR32(Char, First, Len) \ + if (Char < 0x80) { \ + First = 0; \ + Len = 1; \ + } \ + else if (Char < 0x800) { \ + First = 0xc0; \ + Len = 2; \ + } \ + else if (Char < 0x10000) { \ + First = 0xe0; \ + Len = 3; \ + } \ + else if (Char < 0x200000) { \ + First = 0xf0; \ + Len = 4; \ + } \ + else if (Char < 0x4000000) { \ + First = 0xf8; \ + Len = 5; \ + } \ + else { \ + First = 0xfc; \ + Len = 6; \ + } \ + (void)0 - UTF8_COMPUTE(c, mask, len, -1); - if (UNLIKELY(len == -1)) { - /* when called with NULL end, result will never be NULL, - * checks for a NULL character */ - const char *p_next = BLI_str_find_next_char_utf8(p, NULL); - /* will never return the same pointer unless '\0', - * eternal loop is prevented */ - *index += (size_t)(p_next - p); - return BLI_UTF8_ERR; - } +size_t BLI_str_utf8_from_unicode_len(const uint c) +{ + /* If this gets modified, also update the copy in g_string_insert_unichar() */ + uint len = 0; + uint first; - /* this is tricky since there are a few ways we can bail out of bad unicode - * values, 3 possible solutions. */ -#if 0 - UTF8_GET(result, p, i, mask, len, BLI_UTF8_ERR); -#elif 1 - /* WARNING: this is NOT part of glib, or supported by similar functions. - * this is added for text drawing because some filepaths can have latin1 - * characters */ - UTF8_GET(result, p, i, mask, len, BLI_UTF8_ERR); - if (result == BLI_UTF8_ERR) { - len = 1; - result = *p; - } - /* end warning! */ -#else - /* without a fallback like '?', text drawing will stop on this value */ - UTF8_GET(result, p, i, mask, len, '?'); -#endif + UTF8_VARS_FROM_CHAR32(c, first, len); + (void)first; - *index += (size_t)len; - return result; + return len; } -/* was g_unichar_to_utf8 */ /** * BLI_str_utf8_from_unicode: + * * \param c: a Unicode character code - * \param outbuf: output buffer, must have at least 6 bytes of space. - * If %NULL, the length will be computed and returned - * and nothing will be written to outbuf. + * \param outbuf: output buffer, must have at least `outbuf_len` bytes of space. + * If the length required by `c` exceeds `outbuf_len`, + * the bytes available bytes will be zeroed and `outbuf_len` returned. * * Converts a single character to UTF-8. * - * \return number of bytes written + * \return number of bytes written. */ -size_t BLI_str_utf8_from_unicode(uint c, char *outbuf) +size_t BLI_str_utf8_from_unicode(uint c, char *outbuf, const size_t outbuf_len) + { /* If this gets modified, also update the copy in g_string_insert_unichar() */ uint len = 0; uint first; - uint i; - if (c < 0x80) { - first = 0; - len = 1; - } - else if (c < 0x800) { - first = 0xc0; - len = 2; - } - else if (c < 0x10000) { - first = 0xe0; - len = 3; - } - else if (c < 0x200000) { - first = 0xf0; - len = 4; - } - else if (c < 0x4000000) { - first = 0xf8; - len = 5; - } - else { - first = 0xfc; - len = 6; + UTF8_VARS_FROM_CHAR32(c, first, len); + + if (UNLIKELY(outbuf_len < len)) { + /* NULL terminate instead of writing a partial byte. */ + memset(outbuf, 0x0, outbuf_len); + return outbuf_len; } - if (outbuf) { - for (i = len - 1; i > 0; i--) { - outbuf[i] = (c & 0x3f) | 0x80; - c >>= 6; - } - outbuf[0] = c | first; + for (uint i = len - 1; i > 0; i--) { + outbuf[i] = (c & 0x3f) | 0x80; + c >>= 6; } + outbuf[0] = c | first; return len; } @@ -696,16 +682,18 @@ size_t BLI_str_utf8_as_utf32(char32_t *__restrict dst_w, memset(dst_w, 0xff, sizeof(*dst_w) * maxncpy); #endif - while (*src_c && len != maxlen) { - size_t step = 0; - uint unicode = BLI_str_utf8_as_unicode_and_size(src_c, &step); + const size_t src_c_len = strlen(src_c); + const char *src_c_end = src_c + src_c_len; + size_t index = 0; + while ((index < src_c_len) && (len != maxlen)) { + const uint unicode = BLI_str_utf8_as_unicode_step_or_error(src_c, src_c_len, &index); if (unicode != BLI_UTF8_ERR) { *dst_w = unicode; - src_c += step; } else { *dst_w = '?'; - src_c = BLI_str_find_next_char_utf8(src_c, NULL); + const char *src_c_next = BLI_str_find_next_char_utf8(src_c + index, src_c_end); + index = (size_t)(src_c_next - src_c); } dst_w++; len++; @@ -720,36 +708,19 @@ size_t BLI_str_utf32_as_utf8(char *__restrict dst, const char32_t *__restrict src, const size_t maxncpy) { - const size_t maxlen = maxncpy - 1; - /* 6 is max utf8 length of an unicode char. */ - const int64_t maxlen_secured = (int64_t)maxlen - 6; - size_t len = 0; - BLI_assert(maxncpy != 0); - + size_t len = 0; #ifdef DEBUG_STRSIZE memset(dst, 0xff, sizeof(*dst) * maxncpy); #endif - - while (*src && len <= maxlen_secured) { - len += BLI_str_utf8_from_unicode((uint)*src++, dst + len); + while (*src && len < maxncpy) { + len += BLI_str_utf8_from_unicode((uint)*src++, dst + len, maxncpy - len); } - - /* We have to be more careful for the last six bytes, - * to avoid buffer overflow in case utf8-encoded char would be too long for our dst buffer. */ - while (*src) { - char t[6]; - size_t l = BLI_str_utf8_from_unicode((uint)*src++, t); - BLI_assert(l <= 6); - if (len + l > maxlen) { - break; - } - memcpy(dst + len, t, l); - len += l; - } - dst[len] = '\0'; - + /* Return the correct length when part of the final byte did not fit into the string. */ + while ((len > 0) && UNLIKELY(dst[len - 1] == '\0')) { + len--; + } return len; } @@ -759,7 +730,7 @@ size_t BLI_str_utf32_as_utf8_len(const char32_t *src) size_t len = 0; while (*src) { - len += BLI_str_utf8_from_unicode((uint)*src++, NULL); + len += BLI_str_utf8_from_unicode_len((uint)*src++); } return len; @@ -772,31 +743,33 @@ size_t BLI_str_utf32_as_utf8_len(const char32_t *src) * \param p: pointer to some position within \a str * * Given a position \a p with a UTF-8 encoded string \a str, find the start - * of the previous UTF-8 character starting before. \a p Returns %NULL if no - * UTF-8 characters are present in \a str before \a p + * of the previous UTF-8 character starting before. \a p Returns \a str_start if no + * UTF-8 characters are present in \a str_start before \a p. * * \a p does not have to be at the beginning of a UTF-8 character. No check * is made to see if the character found is actually valid other than * it starts with an appropriate byte. * - * Return value: a pointer to the found character or %NULL. + * \return A pointer to the found character. */ -char *BLI_str_find_prev_char_utf8(const char *str, const char *p) +const char *BLI_str_find_prev_char_utf8(const char *p, const char *str_start) { - for (--p; p >= str; p--) { - if ((*p & 0xc0) != 0x80) { - return (char *)p; + BLI_assert(p >= str_start); + if (str_start < p) { + for (--p; p >= str_start; p--) { + if ((*p & 0xc0) != 0x80) { + return (char *)p; + } } } - return NULL; + return p; } /* was g_utf8_find_next_char */ /** * BLI_str_find_next_char_utf8: * \param p: a pointer to a position within a UTF-8 encoded string - * \param end: a pointer to the byte following the end of the string, - * or %NULL to indicate that the string is nul-terminated. + * \param end: a pointer to the byte following the end of the string. * * Finds the start of the next UTF-8 character in the string after \a p * @@ -804,49 +777,18 @@ char *BLI_str_find_prev_char_utf8(const char *str, const char *p) * is made to see if the character found is actually valid other than * it starts with an appropriate byte. * - * Return value: a pointer to the found character or %NULL - */ -char *BLI_str_find_next_char_utf8(const char *p, const char *end) -{ - if (*p) { - if (end) { - for (++p; p < end && (*p & 0xc0) == 0x80; p++) { - /* do nothing */ - } - } - else { - for (++p; (*p & 0xc0) == 0x80; p++) { - /* do nothing */ - } - } - } - return (p == end) ? NULL : (char *)p; -} - -/* was g_utf8_prev_char */ -/** - * BLI_str_prev_char_utf8: - * \param p: a pointer to a position within a UTF-8 encoded string - * - * Finds the previous UTF-8 character in the string before \a p - * - * \a p does not have to be at the beginning of a UTF-8 character. No check - * is made to see if the character found is actually valid other than - * it starts with an appropriate byte. If \a p might be the first - * character of the string, you must use g_utf8_find_prev_char() instead. - * - * Return value: a pointer to the found character. + * \return a pointer to the found character or a pointer to the null terminating character '\0'. */ -char *BLI_str_prev_char_utf8(const char *p) +const char *BLI_str_find_next_char_utf8(const char *p, const char *str_end) { - while (1) { - p--; - if ((*p & 0xc0) != 0x80) { - return (char *)p; + BLI_assert(p <= str_end); + if ((p < str_end) && (*p != '\0')) { + for (++p; p < str_end && (*p & 0xc0) == 0x80; p++) { + /* do nothing */ } } + return p; } -/* end glib copy */ size_t BLI_str_partition_utf8(const char *str, const uint delim[], @@ -871,27 +813,31 @@ size_t BLI_str_partition_ex_utf8(const char *str, const char **suf, const bool from_right) { - const uint *d; const size_t str_len = end ? (size_t)(end - str) : strlen(str); - size_t index; + if (end == NULL) { + end = str + str_len; + } /* Note that here, we assume end points to a valid utf8 char! */ - BLI_assert(end == NULL || (end >= str && (BLI_str_utf8_as_unicode(end) != BLI_UTF8_ERR))); + BLI_assert((end >= str) && (BLI_str_utf8_as_unicode(end) != BLI_UTF8_ERR)); *suf = (char *)(str + str_len); - for (*sep = (char *)(from_right ? BLI_str_find_prev_char_utf8(str, str + str_len) : str), - index = 0; - *sep >= str && (!end || *sep < end) && **sep != '\0'; - *sep = (char *)(from_right ? BLI_str_find_prev_char_utf8(str, *sep) : str + index)) { - const uint c = BLI_str_utf8_as_unicode_and_size(*sep, &index); + size_t index; + for (*sep = (char *)(from_right ? BLI_str_find_prev_char_utf8(end, str) : str), index = 0; + from_right ? (*sep > str) : ((*sep < end) && (**sep != '\0')); + *sep = (char *)(from_right ? (str != *sep ? BLI_str_find_prev_char_utf8(*sep, str) : NULL) : + str + index)) { + size_t index_ofs = 0; + const uint c = BLI_str_utf8_as_unicode_step_or_error(*sep, (size_t)(end - *sep), &index_ofs); + index += index_ofs; if (c == BLI_UTF8_ERR) { *suf = *sep = NULL; break; } - for (d = delim; *d != '\0'; d++) { + for (const uint *d = delim; *d != '\0'; d++) { if (*d == c) { /* *suf is already correct in case from_right is true. */ if (!from_right) { diff --git a/source/blender/blenlib/intern/string_utils.c b/source/blender/blenlib/intern/string_utils.c index d2666c6fe63..bd733aca4f1 100644 --- a/source/blender/blenlib/intern/string_utils.c +++ b/source/blender/blenlib/intern/string_utils.c @@ -155,11 +155,12 @@ void BLI_string_split_prefix(const char *string, char *r_pre, char *r_body, cons * \param from_name: original name, * assumed to be a pointer to a string of at least \a name_len size. * \param strip_number: If set, remove number extensions. + * \return The number of bytes written into \a r_name. */ -void BLI_string_flip_side_name(char *r_name, - const char *from_name, - const bool strip_number, - const size_t name_len) +size_t BLI_string_flip_side_name(char *r_name, + const char *from_name, + const bool strip_number, + const size_t name_len) { size_t len; char *prefix = alloca(name_len); /* The part before the facing */ @@ -172,12 +173,10 @@ void BLI_string_flip_side_name(char *r_name, *prefix = *suffix = *replace = *number = '\0'; /* always copy the name, since this can be called with an uninitialized string */ - BLI_strncpy(r_name, from_name, name_len); - - len = BLI_strnlen(from_name, name_len); + len = BLI_strncpy_rlen(r_name, from_name, name_len); if (len < 3) { /* we don't do names like .R or .L */ - return; + return len; } /* We first check the case with a .### extension, let's find the last period */ @@ -274,7 +273,7 @@ void BLI_string_flip_side_name(char *r_name, } } - BLI_snprintf(r_name, name_len, "%s%s%s%s", prefix, replace, suffix, number); + return BLI_snprintf_rlen(r_name, name_len, "%s%s%s%s", prefix, replace, suffix, number); } /* Unique name utils. */ diff --git a/source/blender/blenlib/intern/uuid.cc b/source/blender/blenlib/intern/uuid.cc new file mode 100644 index 00000000000..ae34bcb3d32 --- /dev/null +++ b/source/blender/blenlib/intern/uuid.cc @@ -0,0 +1,139 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup bli + */ + +#include "BLI_uuid.h" + +#include <cstdio> +#include <cstring> +#include <ctime> +#include <random> +#include <string> + +/* Ensure the UUID struct doesn't have any padding, to be compatible with memcmp(). */ +static_assert(sizeof(bUUID) == 16, "expect UUIDs to be 128 bit exactly"); + +bUUID BLI_uuid_generate_random() +{ + static std::mt19937_64 rng = []() { + std::mt19937_64 rng; + + /* Ensure the RNG really can output 64-bit values. */ + static_assert(std::mt19937_64::min() == 0LL); + static_assert(std::mt19937_64::max() == 0xffffffffffffffffLL); + + struct timespec ts; +#ifdef __APPLE__ + /* `timespec_get()` is only available on macOS 10.15+, so until that's the minimum version + * supported by Blender, use another function to get the timespec. + * + * `clock_gettime()` is only available on POSIX, so not on Windows; Linux uses the newer C++11 + * function `timespec_get()` as well. */ + clock_gettime(CLOCK_REALTIME, &ts); +#else + timespec_get(&ts, TIME_UTC); +#endif + /* XOR the nanosecond and second fields, just in case the clock only has seconds resolution. */ + uint64_t seed = ts.tv_nsec; + seed ^= ts.tv_sec; + rng.seed(seed); + + return rng; + }(); + + bUUID uuid; + + /* RFC4122 suggests setting certain bits to a fixed value, and then randomizing the remaining + * bits. The opposite is easier to implement, though, so that's what's done here. */ + + /* Read two 64-bit numbers to randomize all 128 bits of the UUID. */ + uint64_t *uuid_as_int64 = reinterpret_cast<uint64_t *>(&uuid); + uuid_as_int64[0] = rng(); + uuid_as_int64[1] = rng(); + + /* Set the most significant four bits to 0b0100 to indicate version 4 (random UUID). */ + uuid.time_hi_and_version &= ~0xF000; + uuid.time_hi_and_version |= 0x4000; + + /* Set the most significant two bits to 0b10 to indicate compatibility with RFC4122. */ + uuid.clock_seq_hi_and_reserved &= ~0x40; + uuid.clock_seq_hi_and_reserved |= 0x80; + + return uuid; +} + +bUUID BLI_uuid_nil(void) +{ + const bUUID nil = {0, 0, 0, 0, 0, 0}; + return nil; +} + +bool BLI_uuid_is_nil(bUUID uuid) +{ + return BLI_uuid_equal(BLI_uuid_nil(), uuid); +} + +bool BLI_uuid_equal(const bUUID uuid1, const bUUID uuid2) +{ + return std::memcmp(&uuid1, &uuid2, sizeof(uuid1)) == 0; +} + +void BLI_uuid_format(char *buffer, const bUUID uuid) +{ + std::sprintf(buffer, + "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + uuid.time_low, + uuid.time_mid, + uuid.time_hi_and_version, + uuid.clock_seq_hi_and_reserved, + uuid.clock_seq_low, + uuid.node[0], + uuid.node[1], + uuid.node[2], + uuid.node[3], + uuid.node[4], + uuid.node[5]); +} + +bool BLI_uuid_parse_string(bUUID *uuid, const char *buffer) +{ + const int num_fields_parsed = std::sscanf( + buffer, + "%8x-%4hx-%4hx-%2hhx%2hhx-%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx", + &uuid->time_low, + &uuid->time_mid, + &uuid->time_hi_and_version, + &uuid->clock_seq_hi_and_reserved, + &uuid->clock_seq_low, + &uuid->node[0], + &uuid->node[1], + &uuid->node[2], + &uuid->node[3], + &uuid->node[4], + &uuid->node[5]); + return num_fields_parsed == 11; +} + +std::ostream &operator<<(std::ostream &stream, bUUID uuid) +{ + std::string buffer(36, '\0'); + BLI_uuid_format(buffer.data(), uuid); + stream << buffer; + return stream; +} diff --git a/source/blender/blenlib/intern/winstuff_dir.c b/source/blender/blenlib/intern/winstuff_dir.c index 2dc41bfc54c..6f99ea075bb 100644 --- a/source/blender/blenlib/intern/winstuff_dir.c +++ b/source/blender/blenlib/intern/winstuff_dir.c @@ -24,7 +24,7 @@ #ifdef WIN32 -/* standalone for inclusion in binaries other than blender */ +/* Standalone for inclusion in binaries other than Blender. */ # ifdef USE_STANDALONE # define MEM_mallocN(size, str) ((void)str, malloc(size)) # define MEM_callocN(size, str) ((void)str, calloc(size, 1)) @@ -33,7 +33,7 @@ # include "MEM_guardedalloc.h" # endif -# define WIN32_SKIP_HKEY_PROTECTION // need to use HKEY +# define WIN32_SKIP_HKEY_PROTECTION /* Need to use `HKEY`. */ # include "BLI_utildefines.h" # include "BLI_winstuff.h" # include "utfconv.h" |