Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSiddhartha Jejurkar <f20180617@goa.bits-pilani.ac.in>2021-09-22 18:37:15 +0300
committerSiddhartha Jejurkar <f20180617@goa.bits-pilani.ac.in>2021-09-22 18:37:15 +0300
commit1b9dd08d0281f00ba96b2e6b74c7c9b662663c1b (patch)
treed771024f234cb9fc0984235174a254ed6c38d8c4 /source/blender/blenlib/intern
parent0c2bc843f500cc0bc9511b5ca68ab61a8922ce9b (diff)
parent9f6313498a0af386f08ed17c83bf33b8c2c3b5b3 (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.c243
-rw-r--r--source/blender/blenlib/intern/filereader_file.c80
-rw-r--r--source/blender/blenlib/intern/filereader_gzip.c108
-rw-r--r--source/blender/blenlib/intern/filereader_memory.c145
-rw-r--r--source/blender/blenlib/intern/filereader_zstd.c335
-rw-r--r--source/blender/blenlib/intern/freetypefont.c39
-rw-r--r--source/blender/blenlib/intern/index_mask.cc57
-rw-r--r--source/blender/blenlib/intern/math_base_inline.c20
-rw-r--r--source/blender/blenlib/intern/noise.cc693
-rw-r--r--source/blender/blenlib/intern/string.c194
-rw-r--r--source/blender/blenlib/intern/string_cursor_utf8.c33
-rw-r--r--source/blender/blenlib/intern/string_search.cc17
-rw-r--r--source/blender/blenlib/intern/string_utf8.c364
-rw-r--r--source/blender/blenlib/intern/string_utils.c17
-rw-r--r--source/blender/blenlib/intern/uuid.cc139
-rw-r--r--source/blender/blenlib/intern/winstuff_dir.c4
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"