diff --git a/.gitmodules b/.gitmodules
index 7a2c5600e65..f7dcf5f4ac1 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -93,7 +93,7 @@
url = https://github.com/ClickHouse-Extras/libunwind.git
[submodule "contrib/simdjson"]
path = contrib/simdjson
- url = https://github.com/ClickHouse-Extras/simdjson.git
+ url = https://github.com/simdjson/simdjson.git
[submodule "contrib/rapidjson"]
path = contrib/rapidjson
url = https://github.com/ClickHouse-Extras/rapidjson
@@ -133,7 +133,7 @@
url = https://github.com/unicode-org/icu.git
[submodule "contrib/flatbuffers"]
path = contrib/flatbuffers
- url = https://github.com/google/flatbuffers.git
+ url = https://github.com/ClickHouse-Extras/flatbuffers.git
[submodule "contrib/libc-headers"]
path = contrib/libc-headers
url = https://github.com/ClickHouse-Extras/libc-headers.git
@@ -221,3 +221,9 @@
[submodule "contrib/NuRaft"]
path = contrib/NuRaft
url = https://github.com/ClickHouse-Extras/NuRaft.git
+[submodule "contrib/nanodbc"]
+ path = contrib/nanodbc
+ url = https://github.com/ClickHouse-Extras/nanodbc.git
+[submodule "contrib/datasketches-cpp"]
+ path = contrib/datasketches-cpp
+ url = https://github.com/ClickHouse-Extras/datasketches-cpp.git
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 928991dc937..cc1ec835a7b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,157 @@
-## ClickHouse release 21.3
+## ClickHouse release 21.4
+
+### ClickHouse release 21.4.1 2021-04-12
+
+#### Backward Incompatible Change
+
+* The `toStartOfIntervalFunction` will align hour intervals to the midnight (in previous versions they were aligned to the start of unix epoch). For example, `toStartOfInterval(x, INTERVAL 11 HOUR)` will split every day into three intervals: `00:00:00..10:59:59`, `11:00:00..21:59:59` and `22:00:00..23:59:59`. This behaviour is more suited for practical needs. This closes [#9510](https://github.com/ClickHouse/ClickHouse/issues/9510). [#22060](https://github.com/ClickHouse/ClickHouse/pull/22060) ([alexey-milovidov](https://github.com/alexey-milovidov)).
+* `Age` and `Precision` in graphite rollup configs should increase from retention to retention. Now it's checked and the wrong config raises an exception. [#21496](https://github.com/ClickHouse/ClickHouse/pull/21496) ([Mikhail f. Shiryaev](https://github.com/Felixoid)).
+* Fix `cutToFirstSignificantSubdomainCustom()`/`firstSignificantSubdomainCustom()` returning wrong result for 3+ level domains present in custom top-level domain list. For input domains matching these custom top-level domains, the third-level domain was considered to be the first significant one. This is now fixed. This change may introduce incompatibility if the function is used in e.g. the sharding key. [#21946](https://github.com/ClickHouse/ClickHouse/pull/21946) ([Azat Khuzhin](https://github.com/azat)).
+* Column `keys` in table `system.dictionaries` was replaced to columns `key.names` and `key.types`. Columns `key.names`, `key.types`, `attribute.names`, `attribute.types` from `system.dictionaries` table does not require dictionary to be loaded. [#21884](https://github.com/ClickHouse/ClickHouse/pull/21884) ([Maksim Kita](https://github.com/kitaisreal)).
+* Now replicas that are processing the `ALTER TABLE ATTACH PART[ITION]` command search in their `detached/` folders before fetching the data from other replicas. As an implementation detail, a new command `ATTACH_PART` is introduced in the replicated log. Parts are searched and compared by their checksums. [#18978](https://github.com/ClickHouse/ClickHouse/pull/18978) ([Mike Kot](https://github.com/myrrc)). **Note**:
+ * `ATTACH PART[ITION]` queries may not work during cluster upgrade.
+ * It's not possible to rollback to older ClickHouse version after executing `ALTER ... ATTACH` query in new version as the old servers would fail to pass the `ATTACH_PART` entry in the replicated log.
+* In this version, empty `` will block all access to remote hosts while in previous versions it did nothing. If you want to keep old behaviour and you have empty `remote_url_allow_hosts` element in configuration file, remove it. [#20058](https://github.com/ClickHouse/ClickHouse/pull/20058) ([Vladimir Chebotarev](https://github.com/excitoon)).
+
+
+#### New Feature
+
+* Extended range of `DateTime64` to support dates from year 1925 to 2283. Improved support of `DateTime` around zero date (`1970-01-01`). [#9404](https://github.com/ClickHouse/ClickHouse/pull/9404) ([alexey-milovidov](https://github.com/alexey-milovidov), [Vasily Nemkov](https://github.com/Enmk)). Not every time and date functions are working for extended range of dates.
+* Added support of Kerberos authentication for preconfigured users and HTTP requests (GSS-SPNEGO). [#14995](https://github.com/ClickHouse/ClickHouse/pull/14995) ([Denis Glazachev](https://github.com/traceon)).
+* Add `prefer_column_name_to_alias` setting to use original column names instead of aliases. it is needed to be more compatible with common databases' aliasing rules. This is for [#9715](https://github.com/ClickHouse/ClickHouse/issues/9715) and [#9887](https://github.com/ClickHouse/ClickHouse/issues/9887). [#22044](https://github.com/ClickHouse/ClickHouse/pull/22044) ([Amos Bird](https://github.com/amosbird)).
+* Added functions `dictGetChildren(dictionary, key)`, `dictGetDescendants(dictionary, key, level)`. Function `dictGetChildren` return all children as an array if indexes. It is a inverse transformation for `dictGetHierarchy`. Function `dictGetDescendants` return all descendants as if `dictGetChildren` was applied `level` times recursively. Zero `level` value is equivalent to infinity. Closes [#14656](https://github.com/ClickHouse/ClickHouse/issues/14656). [#22096](https://github.com/ClickHouse/ClickHouse/pull/22096) ([Maksim Kita](https://github.com/kitaisreal)).
+* Added `executable_pool` dictionary source. Close [#14528](https://github.com/ClickHouse/ClickHouse/issues/14528). [#21321](https://github.com/ClickHouse/ClickHouse/pull/21321) ([Maksim Kita](https://github.com/kitaisreal)).
+* Added table function `dictionary`. It works the same way as `Dictionary` engine. Closes [#21560](https://github.com/ClickHouse/ClickHouse/issues/21560). [#21910](https://github.com/ClickHouse/ClickHouse/pull/21910) ([Maksim Kita](https://github.com/kitaisreal)).
+* Support `Nullable` type for `PolygonDictionary` attribute. [#21890](https://github.com/ClickHouse/ClickHouse/pull/21890) ([Maksim Kita](https://github.com/kitaisreal)).
+* Functions `dictGet`, `dictHas` use current database name if it is not specified for dictionaries created with DDL. Closes [#21632](https://github.com/ClickHouse/ClickHouse/issues/21632). [#21859](https://github.com/ClickHouse/ClickHouse/pull/21859) ([Maksim Kita](https://github.com/kitaisreal)).
+* Added function `dictGetOrNull`. It works like `dictGet`, but return `Null` in case key was not found in dictionary. Closes [#22375](https://github.com/ClickHouse/ClickHouse/issues/22375). [#22413](https://github.com/ClickHouse/ClickHouse/pull/22413) ([Maksim Kita](https://github.com/kitaisreal)).
+* Added async update in `ComplexKeyCache`, `SSDCache`, `SSDComplexKeyCache` dictionaries. Added support for `Nullable` type in `Cache`, `ComplexKeyCache`, `SSDCache`, `SSDComplexKeyCache` dictionaries. Added support for multiple attributes fetch with `dictGet`, `dictGetOrDefault` functions. Fixes [#21517](https://github.com/ClickHouse/ClickHouse/issues/21517). [#20595](https://github.com/ClickHouse/ClickHouse/pull/20595) ([Maksim Kita](https://github.com/kitaisreal)).
+* Support `dictHas` function for `RangeHashedDictionary`. Fixes [#6680](https://github.com/ClickHouse/ClickHouse/issues/6680). [#19816](https://github.com/ClickHouse/ClickHouse/pull/19816) ([Maksim Kita](https://github.com/kitaisreal)).
+* Add function `timezoneOf` that returns the timezone name of `DateTime` or `DateTime64` data types. This does not close [#9959](https://github.com/ClickHouse/ClickHouse/issues/9959). Fix inconsistencies in function names: add aliases `timezone` and `timeZone` as well as `toTimezone` and `toTimeZone` and `timezoneOf` and `timeZoneOf`. [#22001](https://github.com/ClickHouse/ClickHouse/pull/22001) ([alexey-milovidov](https://github.com/alexey-milovidov)).
+* Add new optional clause `GRANTEES` for `CREATE/ALTER USER` commands. It specifies users or roles which are allowed to receive grants from this user on condition this user has also all required access granted with grant option. By default `GRANTEES ANY` is used which means a user with grant option can grant to anyone. Syntax: `CREATE USER ... GRANTEES {user | role | ANY | NONE} [,...] [EXCEPT {user | role} [,...]]`. [#21641](https://github.com/ClickHouse/ClickHouse/pull/21641) ([Vitaly Baranov](https://github.com/vitlibar)).
+* Add new column `slowdowns_count` to `system.clusters`. When using hedged requests, it shows how many times we switched to another replica because this replica was responding slowly. Also show actual value of `errors_count` in `system.clusters`. [#21480](https://github.com/ClickHouse/ClickHouse/pull/21480) ([Kruglov Pavel](https://github.com/Avogar)).
+* Add `_partition_id` virtual column for `MergeTree*` engines. Allow to prune partitions by `_partition_id`. Add `partitionID()` function to calculate partition id string. [#21401](https://github.com/ClickHouse/ClickHouse/pull/21401) ([Amos Bird](https://github.com/amosbird)).
+* Add function `isIPAddressInRange` to test if an IPv4 or IPv6 address is contained in a given CIDR network prefix. [#21329](https://github.com/ClickHouse/ClickHouse/pull/21329) ([PHO](https://github.com/depressed-pho)).
+* Added new SQL command `ALTER TABLE 'table_name' UNFREEZE [PARTITION 'part_expr'] WITH NAME 'backup_name'`. This command is needed to properly remove 'freezed' partitions from all disks. [#21142](https://github.com/ClickHouse/ClickHouse/pull/21142) ([Pavel Kovalenko](https://github.com/Jokser)).
+* Supports implicit key type conversion for JOIN. [#19885](https://github.com/ClickHouse/ClickHouse/pull/19885) ([Vladimir](https://github.com/vdimir)).
+
+#### Experimental Feature
+
+* Support `RANGE OFFSET` frame (for window functions) for floating point types. Implement `lagInFrame`/`leadInFrame` window functions, which are analogous to `lag`/`lead`, but respect the window frame. They are identical when the frame is `between unbounded preceding and unbounded following`. This closes [#5485](https://github.com/ClickHouse/ClickHouse/issues/5485). [#21895](https://github.com/ClickHouse/ClickHouse/pull/21895) ([Alexander Kuzmenkov](https://github.com/akuzm)).
+* Zero-copy replication for `ReplicatedMergeTree` over S3 storage. [#16240](https://github.com/ClickHouse/ClickHouse/pull/16240) ([ianton-ru](https://github.com/ianton-ru)).
+* Added possibility to migrate existing S3 disk to the schema with backup-restore capabilities. [#22070](https://github.com/ClickHouse/ClickHouse/pull/22070) ([Pavel Kovalenko](https://github.com/Jokser)).
+
+#### Performance Improvement
+
+* Supported parallel formatting in `clickhouse-local` and everywhere else. [#21630](https://github.com/ClickHouse/ClickHouse/pull/21630) ([Nikita Mikhaylov](https://github.com/nikitamikhaylov)).
+* Support parallel parsing for `CSVWithNames` and `TSVWithNames` formats. This closes [#21085](https://github.com/ClickHouse/ClickHouse/issues/21085). [#21149](https://github.com/ClickHouse/ClickHouse/pull/21149) ([Nikita Mikhaylov](https://github.com/nikitamikhaylov)).
+* Enable read with mmap IO for file ranges from 64 MiB (the settings `min_bytes_to_use_mmap_io`). It may lead to moderate performance improvement. [#22326](https://github.com/ClickHouse/ClickHouse/pull/22326) ([alexey-milovidov](https://github.com/alexey-milovidov)).
+* Add cache for files read with `min_bytes_to_use_mmap_io` setting. It makes significant (2x and more) performance improvement when the value of the setting is small by avoiding frequent mmap/munmap calls and the consequent page faults. Note that mmap IO has major drawbacks that makes it less reliable in production (e.g. hung or SIGBUS on faulty disks; less controllable memory usage). Nevertheless it is good in benchmarks. [#22206](https://github.com/ClickHouse/ClickHouse/pull/22206) ([alexey-milovidov](https://github.com/alexey-milovidov)).
+* Avoid unnecessary data copy when using codec `NONE`. Please note that codec `NONE` is mostly useless - it's recommended to always use compression (`LZ4` is by default). Despite the common belief, disabling compression may not improve performance (the opposite effect is possible). The `NONE` codec is useful in some cases: - when data is uncompressable; - for synthetic benchmarks. [#22145](https://github.com/ClickHouse/ClickHouse/pull/22145) ([alexey-milovidov](https://github.com/alexey-milovidov)).
+* Faster `GROUP BY` with small `max_rows_to_group_by` and `group_by_overflow_mode='any'`. [#21856](https://github.com/ClickHouse/ClickHouse/pull/21856) ([Nikolai Kochetov](https://github.com/KochetovNicolai)).
+* Optimize performance of queries like `SELECT ... FINAL ... WHERE`. Now in queries with `FINAL` it's allowed to move to `PREWHERE` columns, which are in sorting key. [#21830](https://github.com/ClickHouse/ClickHouse/pull/21830) ([foolchi](https://github.com/foolchi)).
+* Improved performance by replacing `memcpy` to another implementation. This closes [#18583](https://github.com/ClickHouse/ClickHouse/issues/18583). [#21520](https://github.com/ClickHouse/ClickHouse/pull/21520) ([alexey-milovidov](https://github.com/alexey-milovidov)).
+* Improve performance of aggregation in order of sorting key (with enabled setting `optimize_aggregation_in_order`). [#19401](https://github.com/ClickHouse/ClickHouse/pull/19401) ([Anton Popov](https://github.com/CurtizJ)).
+
+#### Improvement
+
+* Add connection pool for PostgreSQL table/database engine and dictionary source. Should fix [#21444](https://github.com/ClickHouse/ClickHouse/issues/21444). [#21839](https://github.com/ClickHouse/ClickHouse/pull/21839) ([Kseniia Sumarokova](https://github.com/kssenii)).
+* Support non-default table schema for postgres storage/table-function. Closes [#21701](https://github.com/ClickHouse/ClickHouse/issues/21701). [#21711](https://github.com/ClickHouse/ClickHouse/pull/21711) ([Kseniia Sumarokova](https://github.com/kssenii)).
+* Support replicas priority for postgres dictionary source. [#21710](https://github.com/ClickHouse/ClickHouse/pull/21710) ([Kseniia Sumarokova](https://github.com/kssenii)).
+* Introduce a new merge tree setting `min_bytes_to_rebalance_partition_over_jbod` which allows assigning new parts to different disks of a JBOD volume in a balanced way. [#16481](https://github.com/ClickHouse/ClickHouse/pull/16481) ([Amos Bird](https://github.com/amosbird)).
+* Added `Grant`, `Revoke` and `System` values of `query_kind` column for corresponding queries in `system.query_log`. [#21102](https://github.com/ClickHouse/ClickHouse/pull/21102) ([Vasily Nemkov](https://github.com/Enmk)).
+* Allow customizing timeouts for HTTP connections used for replication independently from other HTTP timeouts. [#20088](https://github.com/ClickHouse/ClickHouse/pull/20088) ([nvartolomei](https://github.com/nvartolomei)).
+* Better exception message in client in case of exception while server is writing blocks. In previous versions client may get misleading message like `Data compressed with different methods`. [#22427](https://github.com/ClickHouse/ClickHouse/pull/22427) ([alexey-milovidov](https://github.com/alexey-milovidov)).
+* Fix error `Directory tmp_fetch_XXX already exists` which could happen after failed fetch part. Delete temporary fetch directory if it already exists. Fixes [#14197](https://github.com/ClickHouse/ClickHouse/issues/14197). [#22411](https://github.com/ClickHouse/ClickHouse/pull/22411) ([nvartolomei](https://github.com/nvartolomei)).
+* Fix MSan report for function `range` with `UInt256` argument (support for large integers is experimental). This closes [#22157](https://github.com/ClickHouse/ClickHouse/issues/22157). [#22387](https://github.com/ClickHouse/ClickHouse/pull/22387) ([alexey-milovidov](https://github.com/alexey-milovidov)).
+* Add `current_database` column to `system.processes` table. It contains the current database of the query. [#22365](https://github.com/ClickHouse/ClickHouse/pull/22365) ([Alexander Kuzmenkov](https://github.com/akuzm)).
+* Add case-insensitive history search/navigation and subword movement features to `clickhouse-client`. [#22105](https://github.com/ClickHouse/ClickHouse/pull/22105) ([Amos Bird](https://github.com/amosbird)).
+* If tuple of NULLs, e.g. `(NULL, NULL)` is on the left hand side of `IN` operator with tuples of non-NULLs on the right hand side, e.g. `SELECT (NULL, NULL) IN ((0, 0), (3, 1))` return 0 instead of throwing an exception about incompatible types. The expression may also appear due to optimization of something like `SELECT (NULL, NULL) = (8, 0) OR (NULL, NULL) = (3, 2) OR (NULL, NULL) = (0, 0) OR (NULL, NULL) = (3, 1)`. This closes [#22017](https://github.com/ClickHouse/ClickHouse/issues/22017). [#22063](https://github.com/ClickHouse/ClickHouse/pull/22063) ([alexey-milovidov](https://github.com/alexey-milovidov)).
+* Update used version of simdjson to 0.9.1. This fixes [#21984](https://github.com/ClickHouse/ClickHouse/issues/21984). [#22057](https://github.com/ClickHouse/ClickHouse/pull/22057) ([Vitaly Baranov](https://github.com/vitlibar)).
+* Added case insensitive aliases for `CONNECTION_ID()` and `VERSION()` functions. This fixes [#22028](https://github.com/ClickHouse/ClickHouse/issues/22028). [#22042](https://github.com/ClickHouse/ClickHouse/pull/22042) ([Eugene Klimov](https://github.com/Slach)).
+* Add option `strict_increase` to `windowFunnel` function to calculate each event once (resolve [#21835](https://github.com/ClickHouse/ClickHouse/issues/21835)). [#22025](https://github.com/ClickHouse/ClickHouse/pull/22025) ([Vladimir](https://github.com/vdimir)).
+* If partition key of a `MergeTree` table does not include `Date` or `DateTime` columns but includes exactly one `DateTime64` column, expose its values in the `min_time` and `max_time` columns in `system.parts` and `system.parts_columns` tables. Add `min_time` and `max_time` columns to `system.parts_columns` table (these was inconsistency to the `system.parts` table). This closes [#18244](https://github.com/ClickHouse/ClickHouse/issues/18244). [#22011](https://github.com/ClickHouse/ClickHouse/pull/22011) ([alexey-milovidov](https://github.com/alexey-milovidov)).
+* Supported `replication_alter_partitions_sync=1` setting in `clickhouse-copier` for moving partitions from helping table to destination. Decreased default timeouts. Fixes [#21911](https://github.com/ClickHouse/ClickHouse/issues/21911). [#21912](https://github.com/ClickHouse/ClickHouse/pull/21912) ([turbo jason](https://github.com/songenjie)).
+* Show path to data directory of `EmbeddedRocksDB` tables in system tables. [#21903](https://github.com/ClickHouse/ClickHouse/pull/21903) ([tavplubix](https://github.com/tavplubix)).
+* Add profile event `HedgedRequestsChangeReplica`, change read data timeout from sec to ms. [#21886](https://github.com/ClickHouse/ClickHouse/pull/21886) ([Kruglov Pavel](https://github.com/Avogar)).
+* DiskS3 (experimental feature under development). Fixed bug with the impossibility to move directory if the destination is not empty and cache disk is used. [#21837](https://github.com/ClickHouse/ClickHouse/pull/21837) ([Pavel Kovalenko](https://github.com/Jokser)).
+* Better formatting for `Array` and `Map` data types in Web UI. [#21798](https://github.com/ClickHouse/ClickHouse/pull/21798) ([alexey-milovidov](https://github.com/alexey-milovidov)).
+* Update clusters only if their configurations were updated. [#21685](https://github.com/ClickHouse/ClickHouse/pull/21685) ([Kruglov Pavel](https://github.com/Avogar)).
+* Propagate query and session settings for distributed DDL queries. Set `distributed_ddl_entry_format_version` to 2 to enable this. Added `distributed_ddl_output_mode` setting. Supported modes: `none`, `throw` (default), `null_status_on_timeout` and `never_throw`. Miscellaneous fixes and improvements for `Replicated` database engine. [#21535](https://github.com/ClickHouse/ClickHouse/pull/21535) ([tavplubix](https://github.com/tavplubix)).
+* If `PODArray` was instantiated with element size that is neither a fraction or a multiple of 16, buffer overflow was possible. No bugs in current releases exist. [#21533](https://github.com/ClickHouse/ClickHouse/pull/21533) ([alexey-milovidov](https://github.com/alexey-milovidov)).
+* Add `last_error_time`/`last_error_message`/`last_error_stacktrace`/`remote` columns for `system.errors`. [#21529](https://github.com/ClickHouse/ClickHouse/pull/21529) ([Azat Khuzhin](https://github.com/azat)).
+* Add aliases `simpleJSONExtract/simpleJSONHas` to `visitParam/visitParamExtract{UInt, Int, Bool, Float, Raw, String}`. Fixes #21383. [#21519](https://github.com/ClickHouse/ClickHouse/pull/21519) ([fastio](https://github.com/fastio)).
+* Add setting `optimize_skip_unused_shards_limit` to limit the number of sharding key values for `optimize_skip_unused_shards`. [#21512](https://github.com/ClickHouse/ClickHouse/pull/21512) ([Azat Khuzhin](https://github.com/azat)).
+* Improve `clickhouse-format` to not throw exception when there are extra spaces or comment after the last query, and throw exception early with readable message when format `ASTInsertQuery` with data . [#21311](https://github.com/ClickHouse/ClickHouse/pull/21311) ([flynn](https://github.com/ucasFL)).
+* Improve support of integer keys in data type `Map`. [#21157](https://github.com/ClickHouse/ClickHouse/pull/21157) ([Anton Popov](https://github.com/CurtizJ)).
+* MaterializeMySQL: attempt to reconnect to MySQL if the connection is lost. [#20961](https://github.com/ClickHouse/ClickHouse/pull/20961) ([Håvard Kvålen](https://github.com/havardk)).
+* Support more cases to rewrite `CROSS JOIN` to `INNER JOIN`. [#20392](https://github.com/ClickHouse/ClickHouse/pull/20392) ([Vladimir](https://github.com/vdimir)).
+* Do not create empty parts on INSERT when `optimize_on_insert` setting enabled. Fixes [#20304](https://github.com/ClickHouse/ClickHouse/issues/20304). [#20387](https://github.com/ClickHouse/ClickHouse/pull/20387) ([Kruglov Pavel](https://github.com/Avogar)).
+* `MaterializeMySQL`: add minmax skipping index for `_version` column. [#20382](https://github.com/ClickHouse/ClickHouse/pull/20382) ([Stig Bakken](https://github.com/stigsb)).
+* Add option `--backslash` for `clickhouse-format`, which can add a backslash at the end of each line of the formatted query. [#21494](https://github.com/ClickHouse/ClickHouse/pull/21494) ([flynn](https://github.com/ucasFL)).
+* Now clickhouse will not throw `LOGICAL_ERROR` exception when we try to mutate the already covered part. Fixes [#22013](https://github.com/ClickHouse/ClickHouse/issues/22013). [#22291](https://github.com/ClickHouse/ClickHouse/pull/22291) ([alesapin](https://github.com/alesapin)).
+
+#### Bug Fix
+
+* Remove socket from epoll before cancelling packet receiver in `HedgedConnections` to prevent possible race. Fixes [#22161](https://github.com/ClickHouse/ClickHouse/issues/22161). [#22443](https://github.com/ClickHouse/ClickHouse/pull/22443) ([Kruglov Pavel](https://github.com/Avogar)).
+* Add (missing) memory accounting in parallel parsing routines. In previous versions OOM was possible when the resultset contains very large blocks of data. This closes [#22008](https://github.com/ClickHouse/ClickHouse/issues/22008). [#22425](https://github.com/ClickHouse/ClickHouse/pull/22425) ([alexey-milovidov](https://github.com/alexey-milovidov)).
+* Fix exception which may happen when `SELECT` has constant `WHERE` condition and source table has columns which names are digits. [#22270](https://github.com/ClickHouse/ClickHouse/pull/22270) ([LiuNeng](https://github.com/liuneng1994)).
+* Fix query cancellation with `use_hedged_requests=0` and `async_socket_for_remote=1`. [#22183](https://github.com/ClickHouse/ClickHouse/pull/22183) ([Azat Khuzhin](https://github.com/azat)).
+* Fix uncaught exception in `InterserverIOHTTPHandler`. [#22146](https://github.com/ClickHouse/ClickHouse/pull/22146) ([Azat Khuzhin](https://github.com/azat)).
+* Fix docker entrypoint in case `http_port` is not in the config. [#22132](https://github.com/ClickHouse/ClickHouse/pull/22132) ([Ewout](https://github.com/devwout)).
+* Fix error `Invalid number of rows in Chunk` in `JOIN` with `TOTALS` and `arrayJoin`. Closes [#19303](https://github.com/ClickHouse/ClickHouse/issues/19303). [#22129](https://github.com/ClickHouse/ClickHouse/pull/22129) ([Vladimir](https://github.com/vdimir)).
+* Fix the background thread pool name which used to poll message from Kafka. The Kafka engine with the broken thread pool will not consume the message from message queue. [#22122](https://github.com/ClickHouse/ClickHouse/pull/22122) ([fastio](https://github.com/fastio)).
+* Fix waiting for `OPTIMIZE` and `ALTER` queries for `ReplicatedMergeTree` table engines. Now the query will not hang when the table was detached or restarted. [#22118](https://github.com/ClickHouse/ClickHouse/pull/22118) ([alesapin](https://github.com/alesapin)).
+* Disable `async_socket_for_remote`/`use_hedged_requests` for buggy Linux kernels. [#22109](https://github.com/ClickHouse/ClickHouse/pull/22109) ([Azat Khuzhin](https://github.com/azat)).
+* Docker entrypoint: avoid chown of `.` in case when `LOG_PATH` is empty. Closes [#22100](https://github.com/ClickHouse/ClickHouse/issues/22100). [#22102](https://github.com/ClickHouse/ClickHouse/pull/22102) ([filimonov](https://github.com/filimonov)).
+* The function `decrypt` was lacking a check for the minimal size of data encrypted in `AEAD` mode. This closes [#21897](https://github.com/ClickHouse/ClickHouse/issues/21897). [#22064](https://github.com/ClickHouse/ClickHouse/pull/22064) ([alexey-milovidov](https://github.com/alexey-milovidov)).
+* In rare case, merge for `CollapsingMergeTree` may create granule with `index_granularity + 1` rows. Because of this, internal check, added in [#18928](https://github.com/ClickHouse/ClickHouse/issues/18928) (affects 21.2 and 21.3), may fail with error `Incomplete granules are not allowed while blocks are granules size`. This error did not allow parts to merge. [#21976](https://github.com/ClickHouse/ClickHouse/pull/21976) ([Nikolai Kochetov](https://github.com/KochetovNicolai)).
+* Reverted [#15454](https://github.com/ClickHouse/ClickHouse/issues/15454) that may cause significant increase in memory usage while loading external dictionaries of hashed type. This closes [#21935](https://github.com/ClickHouse/ClickHouse/issues/21935). [#21948](https://github.com/ClickHouse/ClickHouse/pull/21948) ([Maksim Kita](https://github.com/kitaisreal)).
+* Prevent hedged connections overlaps (`Unknown packet 9 from server` error). [#21941](https://github.com/ClickHouse/ClickHouse/pull/21941) ([Azat Khuzhin](https://github.com/azat)).
+* Fix reading the HTTP POST request with "multipart/form-data" content type in some cases. [#21936](https://github.com/ClickHouse/ClickHouse/pull/21936) ([Ivan](https://github.com/abyss7)).
+* Fix wrong `ORDER BY` results when a query contains window functions, and optimization for reading in primary key order is applied. Fixes [#21828](https://github.com/ClickHouse/ClickHouse/issues/21828). [#21915](https://github.com/ClickHouse/ClickHouse/pull/21915) ([Alexander Kuzmenkov](https://github.com/akuzm)).
+* Fix deadlock in first catboost model execution. Closes [#13832](https://github.com/ClickHouse/ClickHouse/issues/13832). [#21844](https://github.com/ClickHouse/ClickHouse/pull/21844) ([Kruglov Pavel](https://github.com/Avogar)).
+* Fix incorrect query result (and possible crash) which could happen when `WHERE` or `HAVING` condition is pushed before `GROUP BY`. Fixes [#21773](https://github.com/ClickHouse/ClickHouse/issues/21773). [#21841](https://github.com/ClickHouse/ClickHouse/pull/21841) ([Nikolai Kochetov](https://github.com/KochetovNicolai)).
+* Better error handling and logging in `WriteBufferFromS3`. [#21836](https://github.com/ClickHouse/ClickHouse/pull/21836) ([Pavel Kovalenko](https://github.com/Jokser)).
+* Fix possible crashes in aggregate functions with combinator `Distinct`, while using two-level aggregation. This is a follow-up fix of [#18365](https://github.com/ClickHouse/ClickHouse/pull/18365) . Can only reproduced in production env. [#21818](https://github.com/ClickHouse/ClickHouse/pull/21818) ([Amos Bird](https://github.com/amosbird)).
+* Fix scalar subquery index analysis. This fixes [#21717](https://github.com/ClickHouse/ClickHouse/issues/21717) , which was introduced in [#18896](https://github.com/ClickHouse/ClickHouse/pull/18896). [#21766](https://github.com/ClickHouse/ClickHouse/pull/21766) ([Amos Bird](https://github.com/amosbird)).
+* Fix bug for `ReplicatedMerge` table engines when `ALTER MODIFY COLUMN` query doesn't change the type of `Decimal` column if its size (32 bit or 64 bit) doesn't change. [#21728](https://github.com/ClickHouse/ClickHouse/pull/21728) ([alesapin](https://github.com/alesapin)).
+* Fix possible infinite waiting when concurrent `OPTIMIZE` and `DROP` are run for `ReplicatedMergeTree`. [#21716](https://github.com/ClickHouse/ClickHouse/pull/21716) ([Azat Khuzhin](https://github.com/azat)).
+* Fix function `arrayElement` with type `Map` for constant integer arguments. [#21699](https://github.com/ClickHouse/ClickHouse/pull/21699) ([Anton Popov](https://github.com/CurtizJ)).
+* Fix SIGSEGV on not existing attributes from `ip_trie` with `access_to_key_from_attributes`. [#21692](https://github.com/ClickHouse/ClickHouse/pull/21692) ([Azat Khuzhin](https://github.com/azat)).
+* Server now start accepting connections only after `DDLWorker` and dictionaries initialization. [#21676](https://github.com/ClickHouse/ClickHouse/pull/21676) ([Azat Khuzhin](https://github.com/azat)).
+* Add type conversion for keys of tables of type `Join` (previously led to SIGSEGV). [#21646](https://github.com/ClickHouse/ClickHouse/pull/21646) ([Azat Khuzhin](https://github.com/azat)).
+* Fix distributed requests cancellation (for example simple select from multiple shards with limit, i.e. `select * from remote('127.{2,3}', system.numbers) limit 100`) with `async_socket_for_remote=1`. [#21643](https://github.com/ClickHouse/ClickHouse/pull/21643) ([Azat Khuzhin](https://github.com/azat)).
+* Fix `fsync_part_directory` for horizontal merge. [#21642](https://github.com/ClickHouse/ClickHouse/pull/21642) ([Azat Khuzhin](https://github.com/azat)).
+* Remove unknown columns from joined table in `WHERE` for queries to external database engines (MySQL, PostgreSQL). close [#14614](https://github.com/ClickHouse/ClickHouse/issues/14614), close [#19288](https://github.com/ClickHouse/ClickHouse/issues/19288) (dup), close [#19645](https://github.com/ClickHouse/ClickHouse/issues/19645) (dup). [#21640](https://github.com/ClickHouse/ClickHouse/pull/21640) ([Vladimir](https://github.com/vdimir)).
+* `std::terminate` was called if there is an error writing data into s3. [#21624](https://github.com/ClickHouse/ClickHouse/pull/21624) ([Vladimir](https://github.com/vdimir)).
+* Fix possible error `Cannot find column` when `optimize_skip_unused_shards` is enabled and zero shards are used. [#21579](https://github.com/ClickHouse/ClickHouse/pull/21579) ([Azat Khuzhin](https://github.com/azat)).
+* In case if query has constant `WHERE` condition, and setting `optimize_skip_unused_shards` enabled, all shards may be skipped and query could return incorrect empty result. [#21550](https://github.com/ClickHouse/ClickHouse/pull/21550) ([Amos Bird](https://github.com/amosbird)).
+* Fix table function `clusterAllReplicas` returns wrong `_shard_num`. close [#21481](https://github.com/ClickHouse/ClickHouse/issues/21481). [#21498](https://github.com/ClickHouse/ClickHouse/pull/21498) ([flynn](https://github.com/ucasFL)).
+* Fix that S3 table holds old credentials after config update. [#21457](https://github.com/ClickHouse/ClickHouse/pull/21457) ([Grigory Pervakov](https://github.com/GrigoryPervakov)).
+* Fixed race on SSL object inside `SecureSocket` in Poco. [#21456](https://github.com/ClickHouse/ClickHouse/pull/21456) ([Nikita Mikhaylov](https://github.com/nikitamikhaylov)).
+* Fix `Avro` format parsing for `Kafka`. Fixes [#21437](https://github.com/ClickHouse/ClickHouse/issues/21437). [#21438](https://github.com/ClickHouse/ClickHouse/pull/21438) ([Ilya Golshtein](https://github.com/ilejn)).
+* Fix receive and send timeouts and non-blocking read in secure socket. [#21429](https://github.com/ClickHouse/ClickHouse/pull/21429) ([Kruglov Pavel](https://github.com/Avogar)).
+* `force_drop_table` flag didn't work for `MATERIALIZED VIEW`, it's fixed. Fixes [#18943](https://github.com/ClickHouse/ClickHouse/issues/18943). [#20626](https://github.com/ClickHouse/ClickHouse/pull/20626) ([tavplubix](https://github.com/tavplubix)).
+* Fix name clashes in `PredicateRewriteVisitor`. It caused incorrect `WHERE` filtration after full join. Close [#20497](https://github.com/ClickHouse/ClickHouse/issues/20497). [#20622](https://github.com/ClickHouse/ClickHouse/pull/20622) ([Vladimir](https://github.com/vdimir)).
+
+#### Build/Testing/Packaging Improvement
+
+* Add [Jepsen](https://github.com/jepsen-io/jepsen) tests for ClickHouse Keeper. [#21677](https://github.com/ClickHouse/ClickHouse/pull/21677) ([alesapin](https://github.com/alesapin)).
+* Run stateless tests in parallel in CI. Depends on [#22181](https://github.com/ClickHouse/ClickHouse/issues/22181). [#22300](https://github.com/ClickHouse/ClickHouse/pull/22300) ([alesapin](https://github.com/alesapin)).
+* Enable status check for [SQLancer](https://github.com/sqlancer/sqlancer) CI run. [#22015](https://github.com/ClickHouse/ClickHouse/pull/22015) ([Ilya Yatsishin](https://github.com/qoega)).
+* Multiple preparations for PowerPC builds: Enable the bundled openldap on `ppc64le`. [#22487](https://github.com/ClickHouse/ClickHouse/pull/22487) ([Kfir Itzhak](https://github.com/mastertheknife)). Enable compiling on `ppc64le` with Clang. [#22476](https://github.com/ClickHouse/ClickHouse/pull/22476) ([Kfir Itzhak](https://github.com/mastertheknife)). Fix compiling boost on `ppc64le`. [#22474](https://github.com/ClickHouse/ClickHouse/pull/22474) ([Kfir Itzhak](https://github.com/mastertheknife)). Fix CMake error about internal CMake variable `CMAKE_ASM_COMPILE_OBJECT` not set on `ppc64le`. [#22469](https://github.com/ClickHouse/ClickHouse/pull/22469) ([Kfir Itzhak](https://github.com/mastertheknife)). Fix Fedora/RHEL/CentOS not finding `libclang_rt.builtins` on `ppc64le`. [#22458](https://github.com/ClickHouse/ClickHouse/pull/22458) ([Kfir Itzhak](https://github.com/mastertheknife)). Enable building with `jemalloc` on `ppc64le`. [#22447](https://github.com/ClickHouse/ClickHouse/pull/22447) ([Kfir Itzhak](https://github.com/mastertheknife)). Fix ClickHouse's config embedding and cctz's timezone embedding on `ppc64le`. [#22445](https://github.com/ClickHouse/ClickHouse/pull/22445) ([Kfir Itzhak](https://github.com/mastertheknife)). Fixed compiling on `ppc64le` and use the correct instruction pointer register on `ppc64le`. [#22430](https://github.com/ClickHouse/ClickHouse/pull/22430) ([Kfir Itzhak](https://github.com/mastertheknife)).
+* Re-enable the S3 (AWS) library on `aarch64`. [#22484](https://github.com/ClickHouse/ClickHouse/pull/22484) ([Kfir Itzhak](https://github.com/mastertheknife)).
+* Add `tzdata` to Docker containers because reading `ORC` formats requires it. This closes [#14156](https://github.com/ClickHouse/ClickHouse/issues/14156). [#22000](https://github.com/ClickHouse/ClickHouse/pull/22000) ([alexey-milovidov](https://github.com/alexey-milovidov)).
+* Introduce 2 arguments for `clickhouse-server` image Dockerfile: `deb_location` & `single_binary_location`. [#21977](https://github.com/ClickHouse/ClickHouse/pull/21977) ([filimonov](https://github.com/filimonov)).
+* Allow to use clang-tidy with release builds by enabling assertions if it is used. [#21914](https://github.com/ClickHouse/ClickHouse/pull/21914) ([alexey-milovidov](https://github.com/alexey-milovidov)).
+* Add llvm-12 binaries name to search in cmake scripts. Implicit constants conversions to mute clang warnings. Updated submodules to build with CMake 3.19. Mute recursion in macro expansion in `readpassphrase` library. Deprecated `-fuse-ld` changed to `--ld-path` for clang. [#21597](https://github.com/ClickHouse/ClickHouse/pull/21597) ([Ilya Yatsishin](https://github.com/qoega)).
+* Updating `docker/test/testflows/runner/dockerd-entrypoint.sh` to use Yandex dockerhub-proxy, because Docker Hub has enabled very restrictive rate limits [#21551](https://github.com/ClickHouse/ClickHouse/pull/21551) ([vzakaznikov](https://github.com/vzakaznikov)).
+* Fix macOS shared lib build. [#20184](https://github.com/ClickHouse/ClickHouse/pull/20184) ([nvartolomei](https://github.com/nvartolomei)).
+* Add `ctime` option to `zookeeper-dump-tree`. It allows to dump node creation time. [#21842](https://github.com/ClickHouse/ClickHouse/pull/21842) ([Ilya](https://github.com/HumanUser)).
+
+
+## ClickHouse release 21.3 (LTS)
### ClickHouse release v21.3, 2021-03-12
@@ -26,7 +179,7 @@
#### Experimental feature
* Add experimental `Replicated` database engine. It replicates DDL queries across multiple hosts. [#16193](https://github.com/ClickHouse/ClickHouse/pull/16193) ([tavplubix](https://github.com/tavplubix)).
-* Introduce experimental support for window functions, enabled with `allow_experimental_functions = 1`. This is a preliminary, alpha-quality implementation that is not suitable for production use and will change in backward-incompatible ways in future releases. Please see [the documentation](https://github.com/ClickHouse/ClickHouse/blob/master/docs/en/sql-reference/window-functions/index.md#experimental-window-functions) for the list of supported features. [#20337](https://github.com/ClickHouse/ClickHouse/pull/20337) ([Alexander Kuzmenkov](https://github.com/akuzm)).
+* Introduce experimental support for window functions, enabled with `allow_experimental_window_functions = 1`. This is a preliminary, alpha-quality implementation that is not suitable for production use and will change in backward-incompatible ways in future releases. Please see [the documentation](https://github.com/ClickHouse/ClickHouse/blob/master/docs/en/sql-reference/window-functions/index.md#experimental-window-functions) for the list of supported features. [#20337](https://github.com/ClickHouse/ClickHouse/pull/20337) ([Alexander Kuzmenkov](https://github.com/akuzm)).
* Add the ability to backup/restore metadata files for DiskS3. [#18377](https://github.com/ClickHouse/ClickHouse/pull/18377) ([Pavel Kovalenko](https://github.com/Jokser)).
#### Performance Improvement
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d310f7c298c..736a6577660 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -39,6 +39,8 @@ else()
set(RECONFIGURE_MESSAGE_LEVEL STATUS)
endif()
+enable_language(C CXX ASM)
+
include (cmake/arch.cmake)
include (cmake/target.cmake)
include (cmake/tools.cmake)
@@ -66,17 +68,30 @@ endif ()
include (cmake/find/ccache.cmake)
-option(ENABLE_CHECK_HEAVY_BUILDS "Don't allow C++ translation units to compile too long or to take too much memory while compiling" OFF)
+# Take care to add prlimit in command line before ccache, or else ccache thinks that
+# prlimit is compiler, and clang++ is its input file, and refuses to work with
+# multiple inputs, e.g in ccache log:
+# [2021-03-31T18:06:32.655327 36900] Command line: /usr/bin/ccache prlimit --as=10000000000 --data=5000000000 --cpu=600 /usr/bin/clang++-11 - ...... std=gnu++2a -MD -MT src/CMakeFiles/dbms.dir/Storages/MergeTree/IMergeTreeDataPart.cpp.o -MF src/CMakeFiles/dbms.dir/Storages/MergeTree/IMergeTreeDataPart.cpp.o.d -o src/CMakeFiles/dbms.dir/Storages/MergeTree/IMergeTreeDataPart.cpp.o -c ../src/Storages/MergeTree/IMergeTreeDataPart.cpp
+#
+# [2021-03-31T18:06:32.656704 36900] Multiple input files: /usr/bin/clang++-11 and ../src/Storages/MergeTree/IMergeTreeDataPart.cpp
+#
+# Another way would be to use --ccache-skip option before clang++-11 to make
+# ccache ignore it.
+option(ENABLE_CHECK_HEAVY_BUILDS "Don't allow C++ translation units to compile too long or to take too much memory while compiling." OFF)
if (ENABLE_CHECK_HEAVY_BUILDS)
# set DATA (since RSS does not work since 2.6.x+) to 2G
set (RLIMIT_DATA 5000000000)
# set VIRT (RLIMIT_AS) to 10G (DATA*10)
set (RLIMIT_AS 10000000000)
+ # set CPU time limit to 600 seconds
+ set (RLIMIT_CPU 600)
+
# gcc10/gcc10/clang -fsanitize=memory is too heavy
if (SANITIZE STREQUAL "memory" OR COMPILER_GCC)
set (RLIMIT_DATA 10000000000)
endif()
- set (CMAKE_CXX_COMPILER_LAUNCHER prlimit --as=${RLIMIT_AS} --data=${RLIMIT_DATA} --cpu=600)
+
+ set (CMAKE_CXX_COMPILER_LAUNCHER prlimit --as=${RLIMIT_AS} --data=${RLIMIT_DATA} --cpu=${RLIMIT_CPU} ${CMAKE_CXX_COMPILER_LAUNCHER})
endif ()
if (NOT CMAKE_BUILD_TYPE OR CMAKE_BUILD_TYPE STREQUAL "None")
@@ -248,19 +263,27 @@ if (ARCH_NATIVE)
set (COMPILER_FLAGS "${COMPILER_FLAGS} -march=native")
endif ()
-if (COMPILER_GCC OR COMPILER_CLANG)
- # to make numeric_limits<__int128> works with GCC
- set (_CXX_STANDARD "gnu++2a")
-else()
- set (_CXX_STANDARD "c++2a")
-endif()
+if (${CMAKE_VERSION} VERSION_LESS "3.12.4")
+ # CMake < 3.12 doesn't support setting 20 as a C++ standard version.
+ # We will add C++ standard controlling flag in CMAKE_CXX_FLAGS manually for now.
-# cmake < 3.12 doesn't support 20. We'll set CMAKE_CXX_FLAGS for now
-# set (CMAKE_CXX_STANDARD 20)
-set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=${_CXX_STANDARD}")
+ if (COMPILER_GCC OR COMPILER_CLANG)
+ # to make numeric_limits<__int128> works with GCC
+ set (_CXX_STANDARD "gnu++2a")
+ else ()
+ set (_CXX_STANDARD "c++2a")
+ endif ()
-set (CMAKE_CXX_EXTENSIONS 0) # https://cmake.org/cmake/help/latest/prop_tgt/CXX_EXTENSIONS.html#prop_tgt:CXX_EXTENSIONS
-set (CMAKE_CXX_STANDARD_REQUIRED ON)
+ set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=${_CXX_STANDARD}")
+else ()
+ set (CMAKE_CXX_STANDARD 20)
+ set (CMAKE_CXX_EXTENSIONS ON) # Same as gnu++2a (ON) vs c++2a (OFF): https://cmake.org/cmake/help/latest/prop_tgt/CXX_EXTENSIONS.html
+ set (CMAKE_CXX_STANDARD_REQUIRED ON)
+endif ()
+
+set (CMAKE_C_STANDARD 11)
+set (CMAKE_C_EXTENSIONS ON)
+set (CMAKE_C_STANDARD_REQUIRED ON)
if (COMPILER_GCC OR COMPILER_CLANG)
# Enable C++14 sized global deallocation functions. It should be enabled by setting -std=c++14 but I'm not sure.
@@ -454,6 +477,7 @@ find_contrib_lib(double-conversion) # Must be before parquet
include (cmake/find/ssl.cmake)
include (cmake/find/ldap.cmake) # after ssl
include (cmake/find/icu.cmake)
+include (cmake/find/xz.cmake)
include (cmake/find/zlib.cmake)
include (cmake/find/zstd.cmake)
include (cmake/find/ltdl.cmake) # for odbc
@@ -488,6 +512,7 @@ include (cmake/find/fastops.cmake)
include (cmake/find/odbc.cmake)
include (cmake/find/rocksdb.cmake)
include (cmake/find/libpqxx.cmake)
+include (cmake/find/nanodbc.cmake)
include (cmake/find/nuraft.cmake)
@@ -501,6 +526,7 @@ include (cmake/find/msgpack.cmake)
include (cmake/find/cassandra.cmake)
include (cmake/find/sentry.cmake)
include (cmake/find/stats.cmake)
+include (cmake/find/datasketches.cmake)
set (USE_INTERNAL_CITYHASH_LIBRARY ON CACHE INTERNAL "")
find_contrib_lib(cityhash)
diff --git a/README.md b/README.md
index 3329a98877f..ea9f365a3c6 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@ ClickHouse® is an open-source column-oriented database management system that a
* [Tutorial](https://clickhouse.tech/docs/en/getting_started/tutorial/) shows how to set up and query small ClickHouse cluster.
* [Documentation](https://clickhouse.tech/docs/en/) provides more in-depth information.
* [YouTube channel](https://www.youtube.com/c/ClickHouseDB) has a lot of content about ClickHouse in video format.
-* [Slack](https://join.slack.com/t/clickhousedb/shared_invite/zt-ly9m4w1x-6j7x5Ts_pQZqrctAbRZ3cg) and [Telegram](https://telegram.me/clickhouse_en) allow to chat with ClickHouse users in real-time.
+* [Slack](https://join.slack.com/t/clickhousedb/shared_invite/zt-nwwakmk4-xOJ6cdy0sJC3It8j348~IA) and [Telegram](https://telegram.me/clickhouse_en) allow to chat with ClickHouse users in real-time.
* [Blog](https://clickhouse.yandex/blog/en/) contains various ClickHouse-related articles, as well as announcements and reports about events.
* [Code Browser](https://clickhouse.tech/codebrowser/html_report/ClickHouse/index.html) with syntax highlight and navigation.
* [Contacts](https://clickhouse.tech/#contacts) can help to get your questions answered if there are any.
diff --git a/base/CMakeLists.txt b/base/CMakeLists.txt
index 46bd57eda12..023dcaaccae 100644
--- a/base/CMakeLists.txt
+++ b/base/CMakeLists.txt
@@ -8,6 +8,7 @@ add_subdirectory (loggers)
add_subdirectory (pcg-random)
add_subdirectory (widechar_width)
add_subdirectory (readpassphrase)
+add_subdirectory (bridge)
if (USE_MYSQL)
add_subdirectory (mysqlxx)
diff --git a/base/bridge/CMakeLists.txt b/base/bridge/CMakeLists.txt
new file mode 100644
index 00000000000..20b0b651677
--- /dev/null
+++ b/base/bridge/CMakeLists.txt
@@ -0,0 +1,7 @@
+add_library (bridge
+ IBridge.cpp
+)
+
+target_include_directories (daemon PUBLIC ..)
+target_link_libraries (bridge PRIVATE daemon dbms Poco::Data Poco::Data::ODBC)
+
diff --git a/base/bridge/IBridge.cpp b/base/bridge/IBridge.cpp
new file mode 100644
index 00000000000..b1f71315fef
--- /dev/null
+++ b/base/bridge/IBridge.cpp
@@ -0,0 +1,238 @@
+#include "IBridge.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#if USE_ODBC
+# include
+#endif
+
+
+namespace DB
+{
+
+namespace ErrorCodes
+{
+ extern const int ARGUMENT_OUT_OF_BOUND;
+}
+
+namespace
+{
+ Poco::Net::SocketAddress makeSocketAddress(const std::string & host, UInt16 port, Poco::Logger * log)
+ {
+ Poco::Net::SocketAddress socket_address;
+ try
+ {
+ socket_address = Poco::Net::SocketAddress(host, port);
+ }
+ catch (const Poco::Net::DNSException & e)
+ {
+ const auto code = e.code();
+ if (code == EAI_FAMILY
+#if defined(EAI_ADDRFAMILY)
+ || code == EAI_ADDRFAMILY
+#endif
+ )
+ {
+ LOG_ERROR(log, "Cannot resolve listen_host ({}), error {}: {}. If it is an IPv6 address and your host has disabled IPv6, then consider to specify IPv4 address to listen in element of configuration file. Example: 0.0.0.0", host, e.code(), e.message());
+ }
+
+ throw;
+ }
+ return socket_address;
+ }
+
+ Poco::Net::SocketAddress socketBindListen(Poco::Net::ServerSocket & socket, const std::string & host, UInt16 port, Poco::Logger * log)
+ {
+ auto address = makeSocketAddress(host, port, log);
+#if POCO_VERSION < 0x01080000
+ socket.bind(address, /* reuseAddress = */ true);
+#else
+ socket.bind(address, /* reuseAddress = */ true, /* reusePort = */ false);
+#endif
+
+ socket.listen(/* backlog = */ 64);
+
+ return address;
+ }
+}
+
+
+void IBridge::handleHelp(const std::string &, const std::string &)
+{
+ Poco::Util::HelpFormatter help_formatter(options());
+ help_formatter.setCommand(commandName());
+ help_formatter.setHeader("HTTP-proxy for odbc requests");
+ help_formatter.setUsage("--http-port ");
+ help_formatter.format(std::cerr);
+
+ stopOptionsProcessing();
+}
+
+
+void IBridge::defineOptions(Poco::Util::OptionSet & options)
+{
+ options.addOption(
+ Poco::Util::Option("http-port", "", "port to listen").argument("http-port", true) .binding("http-port"));
+
+ options.addOption(
+ Poco::Util::Option("listen-host", "", "hostname or address to listen, default 127.0.0.1").argument("listen-host").binding("listen-host"));
+
+ options.addOption(
+ Poco::Util::Option("http-timeout", "", "http timeout for socket, default 1800").argument("http-timeout").binding("http-timeout"));
+
+ options.addOption(
+ Poco::Util::Option("max-server-connections", "", "max connections to server, default 1024").argument("max-server-connections").binding("max-server-connections"));
+
+ options.addOption(
+ Poco::Util::Option("keep-alive-timeout", "", "keepalive timeout, default 10").argument("keep-alive-timeout").binding("keep-alive-timeout"));
+
+ options.addOption(
+ Poco::Util::Option("log-level", "", "sets log level, default info") .argument("log-level").binding("logger.level"));
+
+ options.addOption(
+ Poco::Util::Option("log-path", "", "log path for all logs, default console").argument("log-path").binding("logger.log"));
+
+ options.addOption(
+ Poco::Util::Option("err-log-path", "", "err log path for all logs, default no").argument("err-log-path").binding("logger.errorlog"));
+
+ options.addOption(
+ Poco::Util::Option("stdout-path", "", "stdout log path, default console").argument("stdout-path").binding("logger.stdout"));
+
+ options.addOption(
+ Poco::Util::Option("stderr-path", "", "stderr log path, default console").argument("stderr-path").binding("logger.stderr"));
+
+ using Me = std::decay_t;
+
+ options.addOption(
+ Poco::Util::Option("help", "", "produce this help message").binding("help").callback(Poco::Util::OptionCallback(this, &Me::handleHelp)));
+
+ ServerApplication::defineOptions(options); // NOLINT Don't need complex BaseDaemon's .xml config
+}
+
+
+void IBridge::initialize(Application & self)
+{
+ BaseDaemon::closeFDs();
+ is_help = config().has("help");
+
+ if (is_help)
+ return;
+
+ config().setString("logger", bridgeName());
+
+ /// Redirect stdout, stderr to specified files.
+ /// Some libraries and sanitizers write to stderr in case of errors.
+ const auto stdout_path = config().getString("logger.stdout", "");
+ if (!stdout_path.empty())
+ {
+ if (!freopen(stdout_path.c_str(), "a+", stdout))
+ throw Poco::OpenFileException("Cannot attach stdout to " + stdout_path);
+
+ /// Disable buffering for stdout.
+ setbuf(stdout, nullptr);
+ }
+ const auto stderr_path = config().getString("logger.stderr", "");
+ if (!stderr_path.empty())
+ {
+ if (!freopen(stderr_path.c_str(), "a+", stderr))
+ throw Poco::OpenFileException("Cannot attach stderr to " + stderr_path);
+
+ /// Disable buffering for stderr.
+ setbuf(stderr, nullptr);
+ }
+
+ buildLoggers(config(), logger(), self.commandName());
+
+ BaseDaemon::logRevision();
+
+ log = &logger();
+ hostname = config().getString("listen-host", "127.0.0.1");
+ port = config().getUInt("http-port");
+ if (port > 0xFFFF)
+ throw Exception("Out of range 'http-port': " + std::to_string(port), ErrorCodes::ARGUMENT_OUT_OF_BOUND);
+
+ http_timeout = config().getUInt("http-timeout", DEFAULT_HTTP_READ_BUFFER_TIMEOUT);
+ max_server_connections = config().getUInt("max-server-connections", 1024);
+ keep_alive_timeout = config().getUInt("keep-alive-timeout", 10);
+
+ initializeTerminationAndSignalProcessing();
+
+#if USE_ODBC
+ if (bridgeName() == "ODBCBridge")
+ Poco::Data::ODBC::Connector::registerConnector();
+#endif
+
+ ServerApplication::initialize(self); // NOLINT
+}
+
+
+void IBridge::uninitialize()
+{
+ BaseDaemon::uninitialize();
+}
+
+
+int IBridge::main(const std::vector & /*args*/)
+{
+ if (is_help)
+ return Application::EXIT_OK;
+
+ registerFormats();
+ LOG_INFO(log, "Starting up {} on host: {}, port: {}", bridgeName(), hostname, port);
+
+ Poco::Net::ServerSocket socket;
+ auto address = socketBindListen(socket, hostname, port, log);
+ socket.setReceiveTimeout(http_timeout);
+ socket.setSendTimeout(http_timeout);
+
+ Poco::ThreadPool server_pool(3, max_server_connections);
+
+ Poco::Net::HTTPServerParams::Ptr http_params = new Poco::Net::HTTPServerParams;
+ http_params->setTimeout(http_timeout);
+ http_params->setKeepAliveTimeout(keep_alive_timeout);
+
+ auto shared_context = Context::createShared();
+ auto context = Context::createGlobal(shared_context.get());
+ context->makeGlobalContext();
+
+ if (config().has("query_masking_rules"))
+ SensitiveDataMasker::setInstance(std::make_unique(config(), "query_masking_rules"));
+
+ auto server = HTTPServer(
+ context,
+ getHandlerFactoryPtr(context),
+ server_pool,
+ socket,
+ http_params);
+
+ SCOPE_EXIT({
+ LOG_DEBUG(log, "Received termination signal.");
+ LOG_DEBUG(log, "Waiting for current connections to close.");
+
+ server.stop();
+
+ for (size_t count : ext::range(1, 6))
+ {
+ if (server.currentConnections() == 0)
+ break;
+ LOG_DEBUG(log, "Waiting for {} connections, try {}", server.currentConnections(), count);
+ std::this_thread::sleep_for(std::chrono::milliseconds(1000));
+ }
+ });
+
+ server.start();
+ LOG_INFO(log, "Listening http://{}", address.toString());
+
+ waitForTerminationRequest();
+ return Application::EXIT_OK;
+}
+
+}
diff --git a/base/bridge/IBridge.h b/base/bridge/IBridge.h
new file mode 100644
index 00000000000..c64003d9959
--- /dev/null
+++ b/base/bridge/IBridge.h
@@ -0,0 +1,51 @@
+#pragma once
+
+#include
+#include
+#include
+
+#include
+#include
+
+
+namespace DB
+{
+
+/// Class represents base for clickhouse-odbc-bridge and clickhouse-library-bridge servers.
+/// Listens to incoming HTTP POST and GET requests on specified port and host.
+/// Has two handlers '/' for all incoming POST requests and /ping for GET request about service status.
+class IBridge : public BaseDaemon
+{
+
+public:
+ /// Define command line arguments
+ void defineOptions(Poco::Util::OptionSet & options) override;
+
+protected:
+ using HandlerFactoryPtr = std::shared_ptr;
+
+ void initialize(Application & self) override;
+
+ void uninitialize() override;
+
+ int main(const std::vector & args) override;
+
+ virtual std::string bridgeName() const = 0;
+
+ virtual HandlerFactoryPtr getHandlerFactoryPtr(ContextPtr context) const = 0;
+
+ size_t keep_alive_timeout;
+
+private:
+ void handleHelp(const std::string &, const std::string &);
+
+ bool is_help;
+ std::string hostname;
+ size_t port;
+ std::string log_level;
+ size_t max_server_connections;
+ size_t http_timeout;
+
+ Poco::Logger * log;
+};
+}
diff --git a/src/Common/BorrowedObjectPool.h b/base/common/BorrowedObjectPool.h
similarity index 99%
rename from src/Common/BorrowedObjectPool.h
rename to base/common/BorrowedObjectPool.h
index d5263cf92a8..6a90a7e7122 100644
--- a/src/Common/BorrowedObjectPool.h
+++ b/base/common/BorrowedObjectPool.h
@@ -7,8 +7,7 @@
#include
#include
-
-#include
+#include
/** Pool for limited size objects that cannot be used from different threads simultaneously.
* The main use case is to have fixed size of objects that can be reused in difference threads during their lifetime
diff --git a/base/common/CMakeLists.txt b/base/common/CMakeLists.txt
index b4bf4f55466..7dfb9bc10c0 100644
--- a/base/common/CMakeLists.txt
+++ b/base/common/CMakeLists.txt
@@ -47,6 +47,10 @@ endif()
target_include_directories(common PUBLIC .. ${CMAKE_CURRENT_BINARY_DIR}/..)
+if (OS_DARWIN AND NOT MAKE_STATIC_LIBRARIES)
+ target_link_libraries(common PUBLIC -Wl,-U,_inside_main)
+endif()
+
# Allow explicit fallback to readline
if (NOT ENABLE_REPLXX AND ENABLE_READLINE)
message (STATUS "Attempt to fallback to readline explicitly")
diff --git a/base/common/DateLUT.cpp b/base/common/DateLUT.cpp
index 6ff0884701c..d14b63cd70a 100644
--- a/base/common/DateLUT.cpp
+++ b/base/common/DateLUT.cpp
@@ -152,7 +152,7 @@ const DateLUTImpl & DateLUT::getImplementation(const std::string & time_zone) co
auto it = impls.emplace(time_zone, nullptr).first;
if (!it->second)
- it->second = std::make_unique(time_zone);
+ it->second = std::unique_ptr(new DateLUTImpl(time_zone));
return *it->second;
}
diff --git a/base/common/DateLUT.h b/base/common/DateLUT.h
index 93c6cb403e2..378b4360f3b 100644
--- a/base/common/DateLUT.h
+++ b/base/common/DateLUT.h
@@ -32,7 +32,6 @@ public:
return date_lut.getImplementation(time_zone);
}
-
static void setDefaultTimezone(const std::string & time_zone)
{
auto & date_lut = getInstance();
diff --git a/base/common/DateLUTImpl.cpp b/base/common/DateLUTImpl.cpp
index 50620e21b8f..e7faeb63760 100644
--- a/base/common/DateLUTImpl.cpp
+++ b/base/common/DateLUTImpl.cpp
@@ -46,24 +46,41 @@ DateLUTImpl::DateLUTImpl(const std::string & time_zone_)
if (&inside_main)
assert(inside_main);
- size_t i = 0;
- time_t start_of_day = 0;
-
cctz::time_zone cctz_time_zone;
if (!cctz::load_time_zone(time_zone, &cctz_time_zone))
throw Poco::Exception("Cannot load time zone " + time_zone_);
- cctz::time_zone::absolute_lookup start_of_epoch_lookup = cctz_time_zone.lookup(std::chrono::system_clock::from_time_t(start_of_day));
- offset_at_start_of_epoch = start_of_epoch_lookup.offset;
- offset_is_whole_number_of_hours_everytime = true;
+ constexpr cctz::civil_day epoch{1970, 1, 1};
+ constexpr cctz::civil_day lut_start{DATE_LUT_MIN_YEAR, 1, 1};
+ time_t start_of_day;
- cctz::civil_day date{1970, 1, 1};
+ /// Note: it's validated against all timezones in the system.
+ static_assert((epoch - lut_start) == daynum_offset_epoch);
+ offset_at_start_of_epoch = cctz_time_zone.lookup(cctz_time_zone.lookup(epoch).pre).offset;
+ offset_at_start_of_lut = cctz_time_zone.lookup(cctz_time_zone.lookup(lut_start).pre).offset;
+ offset_is_whole_number_of_hours_during_epoch = true;
+
+ cctz::civil_day date = lut_start;
+
+ UInt32 i = 0;
do
{
cctz::time_zone::civil_lookup lookup = cctz_time_zone.lookup(date);
- start_of_day = std::chrono::system_clock::to_time_t(lookup.pre); /// Ambiguity is possible.
+ /// Ambiguity is possible if time was changed backwards at the midnight
+ /// or after midnight time has been changed back to midnight, for example one hour backwards at 01:00
+ /// or after midnight time has been changed to the previous day, for example two hours backwards at 01:00
+ /// Then midnight appears twice. Usually time change happens exactly at 00:00 or 01:00.
+
+ /// If transition did not involve previous day, we should use the first midnight as the start of the day,
+ /// otherwise it's better to use the second midnight.
+
+ std::chrono::time_point start_of_day_time_point = lookup.trans < lookup.post
+ ? lookup.post /* Second midnight appears after transition, so there was a piece of previous day after transition */
+ : lookup.pre;
+
+ start_of_day = std::chrono::system_clock::to_time_t(start_of_day_time_point);
Values & values = lut[i];
values.year = date.year();
@@ -72,7 +89,7 @@ DateLUTImpl::DateLUTImpl(const std::string & time_zone_)
values.day_of_week = getDayOfWeek(date);
values.date = start_of_day;
- assert(values.year >= DATE_LUT_MIN_YEAR && values.year <= DATE_LUT_MAX_YEAR);
+ assert(values.year >= DATE_LUT_MIN_YEAR && values.year <= DATE_LUT_MAX_YEAR + 1);
assert(values.month >= 1 && values.month <= 12);
assert(values.day_of_month >= 1 && values.day_of_month <= 31);
assert(values.day_of_week >= 1 && values.day_of_week <= 7);
@@ -85,50 +102,42 @@ DateLUTImpl::DateLUTImpl(const std::string & time_zone_)
else
values.days_in_month = i != 0 ? lut[i - 1].days_in_month : 31;
- values.time_at_offset_change = 0;
- values.amount_of_offset_change = 0;
+ values.time_at_offset_change_value = 0;
+ values.amount_of_offset_change_value = 0;
- if (start_of_day % 3600)
- offset_is_whole_number_of_hours_everytime = false;
+ if (offset_is_whole_number_of_hours_during_epoch && start_of_day > 0 && start_of_day % 3600)
+ offset_is_whole_number_of_hours_during_epoch = false;
- /// If UTC offset was changed in previous day.
- if (i != 0)
+ /// If UTC offset was changed this day.
+ /// Change in time zone without transition is possible, e.g. Moscow 1991 Sun, 31 Mar, 02:00 MSK to EEST
+ cctz::time_zone::civil_transition transition{};
+ if (cctz_time_zone.next_transition(start_of_day_time_point - std::chrono::seconds(1), &transition)
+ && (cctz::civil_day(transition.from) == date || cctz::civil_day(transition.to) == date)
+ && transition.from != transition.to)
{
- auto amount_of_offset_change_at_prev_day = 86400 - (lut[i].date - lut[i - 1].date);
- if (amount_of_offset_change_at_prev_day)
- {
- lut[i - 1].amount_of_offset_change = amount_of_offset_change_at_prev_day;
+ values.time_at_offset_change_value = (transition.from - cctz::civil_second(date)) / Values::OffsetChangeFactor;
+ values.amount_of_offset_change_value = (transition.to - transition.from) / Values::OffsetChangeFactor;
- const auto utc_offset_at_beginning_of_day = cctz_time_zone.lookup(std::chrono::system_clock::from_time_t(lut[i - 1].date)).offset;
+// std::cerr << time_zone << ", " << date << ": change from " << transition.from << " to " << transition.to << "\n";
+// std::cerr << time_zone << ", " << date << ": change at " << values.time_at_offset_change() << " with " << values.amount_of_offset_change() << "\n";
- /// Find a time (timestamp offset from beginning of day),
- /// when UTC offset was changed. Search is performed with 15-minute granularity, assuming it is enough.
+ /// We don't support too large changes.
+ if (values.amount_of_offset_change_value > 24 * 4)
+ values.amount_of_offset_change_value = 24 * 4;
+ else if (values.amount_of_offset_change_value < -24 * 4)
+ values.amount_of_offset_change_value = -24 * 4;
- time_t time_at_offset_change = 900;
- while (time_at_offset_change < 86400)
- {
- auto utc_offset_at_current_time = cctz_time_zone.lookup(std::chrono::system_clock::from_time_t(
- lut[i - 1].date + time_at_offset_change)).offset;
-
- if (utc_offset_at_current_time != utc_offset_at_beginning_of_day)
- break;
-
- time_at_offset_change += 900;
- }
-
- lut[i - 1].time_at_offset_change = time_at_offset_change;
-
- /// We doesn't support cases when time change results in switching to previous day.
- if (static_cast(lut[i - 1].time_at_offset_change) + static_cast(lut[i - 1].amount_of_offset_change) < 0)
- lut[i - 1].time_at_offset_change = -lut[i - 1].amount_of_offset_change;
- }
+ /// We don't support cases when time change results in switching to previous day.
+ /// Shift the point of time change later.
+ if (values.time_at_offset_change_value + values.amount_of_offset_change_value < 0)
+ values.time_at_offset_change_value = -values.amount_of_offset_change_value;
}
/// Going to next day.
++date;
++i;
}
- while (start_of_day <= DATE_LUT_MAX && i <= DATE_LUT_MAX_DAY_NUM);
+ while (i < DATE_LUT_SIZE && lut[i - 1].year <= DATE_LUT_MAX_YEAR);
/// Fill excessive part of lookup table. This is needed only to simplify handling of overflow cases.
while (i < DATE_LUT_SIZE)
diff --git a/base/common/DateLUTImpl.h b/base/common/DateLUTImpl.h
index 0c7465ec7a5..363f281584e 100644
--- a/base/common/DateLUTImpl.h
+++ b/base/common/DateLUTImpl.h
@@ -5,16 +5,24 @@
#include "types.h"
#include
+#include
#include
+#include
+#define DATE_LUT_MIN_YEAR 1925 /// 1925 since wast majority of timezones changed to 15-minute aligned offsets somewhere in 1924 or earlier.
+#define DATE_LUT_MAX_YEAR 2283 /// Last supported year (complete)
+#define DATE_LUT_YEARS (1 + DATE_LUT_MAX_YEAR - DATE_LUT_MIN_YEAR) /// Number of years in lookup table
+
+#define DATE_LUT_SIZE 0x20000
+
#define DATE_LUT_MAX (0xFFFFFFFFU - 86400)
-#define DATE_LUT_MAX_DAY_NUM (0xFFFFFFFFU / 86400)
-/// Table size is bigger than DATE_LUT_MAX_DAY_NUM to fill all indices within UInt16 range: this allows to remove extra check.
-#define DATE_LUT_SIZE 0x10000
-#define DATE_LUT_MIN_YEAR 1970
-#define DATE_LUT_MAX_YEAR 2106 /// Last supported year (incomplete)
-#define DATE_LUT_YEARS (1 + DATE_LUT_MAX_YEAR - DATE_LUT_MIN_YEAR) /// Number of years in lookup table
+#define DATE_LUT_MAX_DAY_NUM 0xFFFF
+
+/// A constant to add to time_t so every supported time point becomes non-negative and still has the same remainder of division by 3600.
+/// If we treat "remainder of division" operation in the sense of modular arithmetic (not like in C++).
+#define DATE_LUT_ADD ((1970 - DATE_LUT_MIN_YEAR) * 366 * 86400)
+
#if defined(__PPC__)
#if !__clang__
@@ -22,6 +30,7 @@
#endif
#endif
+
/// Flags for toYearWeek() function.
enum class WeekModeFlag : UInt8
{
@@ -37,7 +46,8 @@ using YearWeek = std::pair;
*/
class DateLUTImpl
{
-public:
+private:
+ friend class DateLUT;
explicit DateLUTImpl(const std::string & time_zone);
DateLUTImpl(const DateLUTImpl &) = delete;
@@ -45,14 +55,75 @@ public:
DateLUTImpl(const DateLUTImpl &&) = delete;
DateLUTImpl & operator=(const DateLUTImpl &&) = delete;
+ // Normalized and bound-checked index of element in lut,
+ // has to be a separate type to support overloading
+ // TODO: make sure that any arithmetic on LUTIndex actually results in valid LUTIndex.
+ STRONG_TYPEDEF(UInt32, LUTIndex)
+
+ template
+ friend inline LUTIndex operator+(const LUTIndex & index, const T v)
+ {
+ return LUTIndex{(index.toUnderType() + UInt32(v)) & date_lut_mask};
+ }
+
+ template
+ friend inline LUTIndex operator+(const T v, const LUTIndex & index)
+ {
+ return LUTIndex{(v + index.toUnderType()) & date_lut_mask};
+ }
+
+ friend inline LUTIndex operator+(const LUTIndex & index, const LUTIndex & v)
+ {
+ return LUTIndex{(index.toUnderType() + v.toUnderType()) & date_lut_mask};
+ }
+
+ template
+ friend inline LUTIndex operator-(const LUTIndex & index, const T v)
+ {
+ return LUTIndex{(index.toUnderType() - UInt32(v)) & date_lut_mask};
+ }
+
+ template
+ friend inline LUTIndex operator-(const T v, const LUTIndex & index)
+ {
+ return LUTIndex{(v - index.toUnderType()) & date_lut_mask};
+ }
+
+ friend inline LUTIndex operator-(const LUTIndex & index, const LUTIndex & v)
+ {
+ return LUTIndex{(index.toUnderType() - v.toUnderType()) & date_lut_mask};
+ }
+
+ template
+ friend inline LUTIndex operator*(const LUTIndex & index, const T v)
+ {
+ return LUTIndex{(index.toUnderType() * UInt32(v)) & date_lut_mask};
+ }
+
+ template
+ friend inline LUTIndex operator*(const T v, const LUTIndex & index)
+ {
+ return LUTIndex{(v * index.toUnderType()) & date_lut_mask};
+ }
+
+ template
+ friend inline LUTIndex operator/(const LUTIndex & index, const T v)
+ {
+ return LUTIndex{(index.toUnderType() / UInt32(v)) & date_lut_mask};
+ }
+
+ template
+ friend inline LUTIndex operator/(const T v, const LUTIndex & index)
+ {
+ return LUTIndex{(UInt32(v) / index.toUnderType()) & date_lut_mask};
+ }
+
public:
/// The order of fields matters for alignment and sizeof.
struct Values
{
- /// Least significat 32 bits from time_t at beginning of the day.
- /// If the unix timestamp of beginning of the day is negative (example: 1970-01-01 MSK, where time_t == -10800), then value will overflow.
- /// Change to time_t; change constants above; and recompile the sources if you need to support time after 2105 year.
- UInt32 date;
+ /// time_t at beginning of the day.
+ Int64 date;
/// Properties of the day.
UInt16 year;
@@ -65,107 +136,189 @@ public:
UInt8 days_in_month;
/// For days, when offset from UTC was changed due to daylight saving time or permanent change, following values could be non zero.
- Int16 amount_of_offset_change; /// Usually -3600 or 3600, but look at Lord Howe Island.
- UInt32 time_at_offset_change; /// In seconds from beginning of the day.
+ /// All in OffsetChangeFactor (15 minute) intervals.
+ Int8 amount_of_offset_change_value; /// Usually -4 or 4, but look at Lord Howe Island. Multiply by OffsetChangeFactor
+ UInt8 time_at_offset_change_value; /// In seconds from beginning of the day. Multiply by OffsetChangeFactor
+
+ inline Int32 amount_of_offset_change() const
+ {
+ return static_cast(amount_of_offset_change_value) * OffsetChangeFactor;
+ }
+
+ inline UInt32 time_at_offset_change() const
+ {
+ return static_cast(time_at_offset_change_value) * OffsetChangeFactor;
+ }
+
+ /// Since most of the modern timezones have a DST change aligned to 15 minutes, to save as much space as possible inside Value,
+ /// we are dividing any offset change related value by this factor before setting it to Value,
+ /// hence it has to be explicitly multiplied back by this factor before being used.
+ static constexpr UInt16 OffsetChangeFactor = 900;
};
static_assert(sizeof(Values) == 16);
private:
- /// Lookup table is indexed by DayNum.
+
+ /// Mask is all-ones to allow efficient protection against overflow.
+ static constexpr UInt32 date_lut_mask = 0x1ffff;
+ static_assert(date_lut_mask == DATE_LUT_SIZE - 1);
+
+ /// Offset to epoch in days (ExtendedDayNum) of the first day in LUT.
+ /// "epoch" is the Unix Epoch (starts at unix timestamp zero)
+ static constexpr UInt32 daynum_offset_epoch = 16436;
+ static_assert(daynum_offset_epoch == (1970 - DATE_LUT_MIN_YEAR) * 365 + (1970 - DATE_LUT_MIN_YEAR / 4 * 4) / 4);
+
+ /// Lookup table is indexed by LUTIndex.
/// Day nums are the same in all time zones. 1970-01-01 is 0 and so on.
/// Table is relatively large, so better not to place the object on stack.
/// In comparison to std::vector, plain array is cheaper by one indirection.
- Values lut[DATE_LUT_SIZE];
+ Values lut[DATE_LUT_SIZE + 1];
- /// Year number after DATE_LUT_MIN_YEAR -> day num for start of year.
- DayNum years_lut[DATE_LUT_YEARS];
+ /// Year number after DATE_LUT_MIN_YEAR -> LUTIndex in lut for start of year.
+ LUTIndex years_lut[DATE_LUT_YEARS];
/// Year number after DATE_LUT_MIN_YEAR * month number starting at zero -> day num for first day of month
- DayNum years_months_lut[DATE_LUT_YEARS * 12];
+ LUTIndex years_months_lut[DATE_LUT_YEARS * 12];
/// UTC offset at beginning of the Unix epoch. The same as unix timestamp of 1970-01-01 00:00:00 local time.
time_t offset_at_start_of_epoch;
- bool offset_is_whole_number_of_hours_everytime;
+ /// UTC offset at the beginning of the first supported year.
+ time_t offset_at_start_of_lut;
+ bool offset_is_whole_number_of_hours_during_epoch;
/// Time zone name.
std::string time_zone;
-
- /// We can correctly process only timestamps that less DATE_LUT_MAX (i.e. up to 2105 year inclusively)
- /// We don't care about overflow.
- inline DayNum findIndex(time_t t) const
+ inline LUTIndex findIndex(time_t t) const
{
/// First guess.
- DayNum guess(t / 86400);
+ Int64 guess = (t / 86400) + daynum_offset_epoch;
+
+ /// For negative time_t the integer division was rounded up, so the guess is offset by one.
+ if (unlikely(t < 0))
+ --guess;
+
+ if (guess < 0)
+ return LUTIndex(0);
+ if (guess >= DATE_LUT_SIZE)
+ return LUTIndex(DATE_LUT_SIZE - 1);
/// UTC offset is from -12 to +14 in all known time zones. This requires checking only three indices.
- if ((guess == 0 || t >= lut[guess].date) && t < lut[DayNum(guess + 1)].date)
- return guess;
+ if (t >= lut[guess].date)
+ {
+ if (guess + 1 >= DATE_LUT_SIZE || t < lut[guess + 1].date)
+ return LUTIndex(guess);
- /// Time zones that have offset 0 from UTC do daylight saving time change (if any) towards increasing UTC offset (example: British Standard Time).
- if (t >= lut[DayNum(guess + 1)].date)
- return DayNum(guess + 1);
+ return LUTIndex(guess + 1);
+ }
- return DayNum(guess - 1);
+ return LUTIndex(guess ? guess - 1 : 0);
}
- inline const Values & find(time_t t) const
+ inline LUTIndex toLUTIndex(DayNum d) const
{
- return lut[findIndex(t)];
+ return LUTIndex{(d + daynum_offset_epoch) & date_lut_mask};
+ }
+
+ inline LUTIndex toLUTIndex(ExtendedDayNum d) const
+ {
+ return LUTIndex{static_cast(d + daynum_offset_epoch) & date_lut_mask};
+ }
+
+ inline LUTIndex toLUTIndex(time_t t) const
+ {
+ return findIndex(t);
+ }
+
+ inline LUTIndex toLUTIndex(LUTIndex i) const
+ {
+ return i;
+ }
+
+ template
+ inline const Values & find(DateOrTime v) const
+ {
+ return lut[toLUTIndex(v)];
+ }
+
+ template
+ static inline T roundDown(T x, Divisor divisor)
+ {
+ static_assert(std::is_integral_v && std::is_integral_v);
+ assert(divisor > 0);
+
+ if (likely(x >= 0))
+ return x / divisor * divisor;
+
+ /// Integer division for negative numbers rounds them towards zero (up).
+ /// We will shift the number so it will be rounded towards -inf (down).
+
+ return (x + 1 - divisor) / divisor * divisor;
}
public:
const std::string & getTimeZone() const { return time_zone; }
+ // Methods only for unit-testing, it makes very little sense to use it from user code.
+ auto getOffsetAtStartOfEpoch() const { return offset_at_start_of_epoch; }
+ auto getTimeOffsetAtStartOfLUT() const { return offset_at_start_of_lut; }
+
/// All functions below are thread-safe; arguments are not checked.
- inline time_t toDate(time_t t) const { return find(t).date; }
- inline unsigned toMonth(time_t t) const { return find(t).month; }
- inline unsigned toQuarter(time_t t) const { return (find(t).month - 1) / 3 + 1; }
- inline unsigned toYear(time_t t) const { return find(t).year; }
- inline unsigned toDayOfWeek(time_t t) const { return find(t).day_of_week; }
- inline unsigned toDayOfMonth(time_t t) const { return find(t).day_of_month; }
+ inline ExtendedDayNum toDayNum(ExtendedDayNum d) const
+ {
+ return d;
+ }
+
+ template
+ inline ExtendedDayNum toDayNum(DateOrTime v) const
+ {
+ return ExtendedDayNum{static_cast(toLUTIndex(v).toUnderType() - daynum_offset_epoch)};
+ }
/// Round down to start of monday.
- inline time_t toFirstDayOfWeek(time_t t) const
+ template
+ inline time_t toFirstDayOfWeek(DateOrTime v) const
{
- DayNum index = findIndex(t);
- return lut[DayNum(index - (lut[index].day_of_week - 1))].date;
+ const LUTIndex i = toLUTIndex(v);
+ return lut[i - (lut[i].day_of_week - 1)].date;
}
- inline DayNum toFirstDayNumOfWeek(DayNum d) const
+ template
+ inline ExtendedDayNum toFirstDayNumOfWeek(DateOrTime v) const
{
- return DayNum(d - (lut[d].day_of_week - 1));
- }
-
- inline DayNum toFirstDayNumOfWeek(time_t t) const
- {
- return toFirstDayNumOfWeek(toDayNum(t));
+ const LUTIndex i = toLUTIndex(v);
+ return toDayNum(i - (lut[i].day_of_week - 1));
}
/// Round down to start of month.
- inline time_t toFirstDayOfMonth(time_t t) const
+ template
+ inline time_t toFirstDayOfMonth(DateOrTime v) const
{
- DayNum index = findIndex(t);
- return lut[index - (lut[index].day_of_month - 1)].date;
+ const LUTIndex i = toLUTIndex(v);
+ return lut[i - (lut[i].day_of_month - 1)].date;
}
- inline DayNum toFirstDayNumOfMonth(DayNum d) const
+ template
+ inline ExtendedDayNum toFirstDayNumOfMonth(DateOrTime v) const
{
- return DayNum(d - (lut[d].day_of_month - 1));
- }
-
- inline DayNum toFirstDayNumOfMonth(time_t t) const
- {
- return toFirstDayNumOfMonth(toDayNum(t));
+ const LUTIndex i = toLUTIndex(v);
+ return toDayNum(i - (lut[i].day_of_month - 1));
}
/// Round down to start of quarter.
- inline DayNum toFirstDayNumOfQuarter(DayNum d) const
+ template
+ inline ExtendedDayNum toFirstDayNumOfQuarter(DateOrTime v) const
{
- DayNum index = d;
+ return toDayNum(toFirstDayOfQuarterIndex(v));
+ }
+
+ template
+ inline LUTIndex toFirstDayOfQuarterIndex(DateOrTime v) const
+ {
+ LUTIndex index = toLUTIndex(v);
size_t month_inside_quarter = (lut[index].month - 1) % 3;
index -= lut[index].day_of_month;
@@ -175,17 +328,13 @@ public:
--month_inside_quarter;
}
- return DayNum(index + 1);
+ return index + 1;
}
- inline DayNum toFirstDayNumOfQuarter(time_t t) const
+ template
+ inline time_t toFirstDayOfQuarter(DateOrTime v) const
{
- return toFirstDayNumOfQuarter(toDayNum(t));
- }
-
- inline time_t toFirstDayOfQuarter(time_t t) const
- {
- return fromDayNum(toFirstDayNumOfQuarter(t));
+ return toDate(toFirstDayOfQuarterIndex(v));
}
/// Round down to start of year.
@@ -194,48 +343,47 @@ public:
return lut[years_lut[lut[findIndex(t)].year - DATE_LUT_MIN_YEAR]].date;
}
- inline DayNum toFirstDayNumOfYear(DayNum d) const
+ template
+ inline LUTIndex toFirstDayNumOfYearIndex(DateOrTime v) const
{
- return years_lut[lut[d].year - DATE_LUT_MIN_YEAR];
+ return years_lut[lut[toLUTIndex(v)].year - DATE_LUT_MIN_YEAR];
}
- inline DayNum toFirstDayNumOfYear(time_t t) const
+ template
+ inline ExtendedDayNum toFirstDayNumOfYear(DateOrTime v) const
{
- return toFirstDayNumOfYear(toDayNum(t));
+ return toDayNum(toFirstDayNumOfYearIndex(v));
}
inline time_t toFirstDayOfNextMonth(time_t t) const
{
- DayNum index = findIndex(t);
+ LUTIndex index = findIndex(t);
index += 32 - lut[index].day_of_month;
return lut[index - (lut[index].day_of_month - 1)].date;
}
inline time_t toFirstDayOfPrevMonth(time_t t) const
{
- DayNum index = findIndex(t);
+ LUTIndex index = findIndex(t);
index -= lut[index].day_of_month;
return lut[index - (lut[index].day_of_month - 1)].date;
}
- inline UInt8 daysInMonth(DayNum d) const
+ template
+ inline UInt8 daysInMonth(DateOrTime value) const
{
- return lut[d].days_in_month;
+ const LUTIndex i = toLUTIndex(value);
+ return lut[i].days_in_month;
}
- inline UInt8 daysInMonth(time_t t) const
- {
- return find(t).days_in_month;
- }
-
- inline UInt8 daysInMonth(UInt16 year, UInt8 month) const
+ inline UInt8 daysInMonth(Int16 year, UInt8 month) const
{
UInt16 idx = year - DATE_LUT_MIN_YEAR;
if (unlikely(idx >= DATE_LUT_YEARS))
return 31; /// Implementation specific behaviour on overflow.
/// 32 makes arithmetic more simple.
- DayNum any_day_of_month = DayNum(years_lut[idx] + 32 * (month - 1));
+ const auto any_day_of_month = years_lut[year - DATE_LUT_MIN_YEAR] + 32 * (month - 1);
return lut[any_day_of_month].days_in_month;
}
@@ -243,107 +391,111 @@ public:
*/
inline time_t toDateAndShift(time_t t, Int32 days) const
{
- return lut[DayNum(findIndex(t) + days)].date;
+ return lut[findIndex(t) + days].date;
}
inline time_t toTime(time_t t) const
{
- DayNum index = findIndex(t);
-
- if (unlikely(index == 0 || index > DATE_LUT_MAX_DAY_NUM))
- return t + offset_at_start_of_epoch;
+ const LUTIndex index = findIndex(t);
time_t res = t - lut[index].date;
- if (res >= lut[index].time_at_offset_change)
- res += lut[index].amount_of_offset_change;
+ if (res >= lut[index].time_at_offset_change())
+ res += lut[index].amount_of_offset_change();
return res - offset_at_start_of_epoch; /// Starting at 1970-01-01 00:00:00 local time.
}
inline unsigned toHour(time_t t) const
{
- DayNum index = findIndex(t);
-
- /// If it is overflow case,
- /// then limit number of hours to avoid insane results like 1970-01-01 89:28:15
- if (unlikely(index == 0 || index > DATE_LUT_MAX_DAY_NUM))
- return static_cast((t + offset_at_start_of_epoch) / 3600) % 24;
+ const LUTIndex index = findIndex(t);
time_t time = t - lut[index].date;
- if (time >= lut[index].time_at_offset_change)
- time += lut[index].amount_of_offset_change;
+ if (time >= lut[index].time_at_offset_change())
+ time += lut[index].amount_of_offset_change();
unsigned res = time / 3600;
- return res <= 23 ? res : 0;
+
+ /// In case time was changed backwards at the start of next day, we will repeat the hour 23.
+ return res <= 23 ? res : 23;
}
/** Calculating offset from UTC in seconds.
- * which means Using the same literal time of "t" to get the corresponding timestamp in UTC,
- * then subtract the former from the latter to get the offset result.
- * The boundaries when meets DST(daylight saving time) change should be handled very carefully.
- */
+ * which means Using the same literal time of "t" to get the corresponding timestamp in UTC,
+ * then subtract the former from the latter to get the offset result.
+ * The boundaries when meets DST(daylight saving time) change should be handled very carefully.
+ */
inline time_t timezoneOffset(time_t t) const
{
- DayNum index = findIndex(t);
+ const LUTIndex index = findIndex(t);
/// Calculate daylight saving offset first.
/// Because the "amount_of_offset_change" in LUT entry only exists in the change day, it's costly to scan it from the very begin.
/// but we can figure out all the accumulated offsets from 1970-01-01 to that day just by get the whole difference between lut[].date,
/// and then, we can directly subtract multiple 86400s to get the real DST offsets for the leap seconds is not considered now.
- time_t res = (lut[index].date - lut[0].date) % 86400;
+ time_t res = (lut[index].date - lut[daynum_offset_epoch].date) % 86400;
+
/// As so far to know, the maximal DST offset couldn't be more than 2 hours, so after the modulo operation the remainder
/// will sits between [-offset --> 0 --> offset] which respectively corresponds to moving clock forward or backward.
res = res > 43200 ? (86400 - res) : (0 - res);
/// Check if has a offset change during this day. Add the change when cross the line
- if (lut[index].amount_of_offset_change != 0 && t >= lut[index].date + lut[index].time_at_offset_change)
- res += lut[index].amount_of_offset_change;
+ if (lut[index].amount_of_offset_change() != 0 && t >= lut[index].date + lut[index].time_at_offset_change())
+ res += lut[index].amount_of_offset_change();
return res + offset_at_start_of_epoch;
}
- /** Only for time zones with/when offset from UTC is multiple of five minutes.
- * This is true for all time zones: right now, all time zones have an offset that is multiple of 15 minutes.
- *
- * "By 1929, most major countries had adopted hourly time zones. Nepal was the last
- * country to adopt a standard offset, shifting slightly to UTC+5:45 in 1986."
- * - https://en.wikipedia.org/wiki/Time_zone#Offsets_from_UTC
- *
- * Also please note, that unix timestamp doesn't count "leap seconds":
- * each minute, with added or subtracted leap second, spans exactly 60 unix timestamps.
- */
- inline unsigned toSecond(time_t t) const { return UInt32(t) % 60; }
+ inline unsigned toSecond(time_t t) const
+ {
+ auto res = t % 60;
+ if (likely(res >= 0))
+ return res;
+ return res + 60;
+ }
inline unsigned toMinute(time_t t) const
{
- if (offset_is_whole_number_of_hours_everytime)
- return (UInt32(t) / 60) % 60;
+ if (t >= 0 && offset_is_whole_number_of_hours_during_epoch)
+ return (t / 60) % 60;
- /// To consider the DST changing situation within this day.
- /// also make the special timezones with no whole hour offset such as 'Australia/Lord_Howe' been taken into account
- DayNum index = findIndex(t);
- UInt32 res = t - lut[index].date;
- if (lut[index].amount_of_offset_change != 0 && t >= lut[index].date + lut[index].time_at_offset_change)
- res += lut[index].amount_of_offset_change;
+ /// To consider the DST changing situation within this day
+ /// also make the special timezones with no whole hour offset such as 'Australia/Lord_Howe' been taken into account.
- return res / 60 % 60;
+ LUTIndex index = findIndex(t);
+ UInt32 time = t - lut[index].date;
+
+ if (time >= lut[index].time_at_offset_change())
+ time += lut[index].amount_of_offset_change();
+
+ return time / 60 % 60;
}
- inline time_t toStartOfMinute(time_t t) const { return t / 60 * 60; }
- inline time_t toStartOfFiveMinute(time_t t) const { return t / 300 * 300; }
- inline time_t toStartOfFifteenMinutes(time_t t) const { return t / 900 * 900; }
- inline time_t toStartOfTenMinutes(time_t t) const { return t / 600 * 600; }
+ /// NOTE: Assuming timezone offset is a multiple of 15 minutes.
+ inline time_t toStartOfMinute(time_t t) const { return roundDown(t, 60); }
+ inline time_t toStartOfFiveMinute(time_t t) const { return roundDown(t, 300); }
+ inline time_t toStartOfFifteenMinutes(time_t t) const { return roundDown(t, 900); }
+ inline time_t toStartOfTenMinutes(time_t t) const
+ {
+ if (t >= 0 && offset_is_whole_number_of_hours_during_epoch)
+ return t / 600 * 600;
+
+ /// More complex logic is for Nepal - it has offset 05:45. Australia/Eucla is also unfortunate.
+ Int64 date = find(t).date;
+ return date + (t - date) / 600 * 600;
+ }
+
+ /// NOTE: Assuming timezone transitions are multiple of hours. Lord Howe Island in Australia is a notable exception.
inline time_t toStartOfHour(time_t t) const
{
- if (offset_is_whole_number_of_hours_everytime)
+ if (t >= 0 && offset_is_whole_number_of_hours_during_epoch)
return t / 3600 * 3600;
- UInt32 date = find(t).date;
- return date + (UInt32(t) - date) / 3600 * 3600;
+ Int64 date = find(t).date;
+ return date + (t - date) / 3600 * 3600;
}
/** Number of calendar day since the beginning of UNIX epoch (1970-01-01 is zero)
@@ -354,80 +506,89 @@ public:
* because the same calendar day starts/ends at different timestamps in different time zones)
*/
- inline DayNum toDayNum(time_t t) const { return findIndex(t); }
- inline time_t fromDayNum(DayNum d) const { return lut[d].date; }
+ inline time_t fromDayNum(DayNum d) const { return lut[toLUTIndex(d)].date; }
+ inline time_t fromDayNum(ExtendedDayNum d) const { return lut[toLUTIndex(d)].date; }
- inline time_t toDate(DayNum d) const { return lut[d].date; }
- inline unsigned toMonth(DayNum d) const { return lut[d].month; }
- inline unsigned toQuarter(DayNum d) const { return (lut[d].month - 1) / 3 + 1; }
- inline unsigned toYear(DayNum d) const { return lut[d].year; }
- inline unsigned toDayOfWeek(DayNum d) const { return lut[d].day_of_week; }
- inline unsigned toDayOfMonth(DayNum d) const { return lut[d].day_of_month; }
- inline unsigned toDayOfYear(DayNum d) const { return d + 1 - toFirstDayNumOfYear(d); }
+ template
+ inline time_t toDate(DateOrTime v) const { return lut[toLUTIndex(v)].date; }
- inline unsigned toDayOfYear(time_t t) const { return toDayOfYear(toDayNum(t)); }
+ template
+ inline unsigned toMonth(DateOrTime v) const { return lut[toLUTIndex(v)].month; }
+
+ template
+ inline unsigned toQuarter(DateOrTime v) const { return (lut[toLUTIndex(v)].month - 1) / 3 + 1; }
+
+ template
+ inline Int16 toYear(DateOrTime v) const { return lut[toLUTIndex(v)].year; }
+
+ template
+ inline unsigned toDayOfWeek(DateOrTime v) const { return lut[toLUTIndex(v)].day_of_week; }
+
+ template
+ inline unsigned toDayOfMonth(DateOrTime v) const { return lut[toLUTIndex(v)].day_of_month; }
+
+ template
+ inline unsigned toDayOfYear(DateOrTime v) const
+ {
+ // TODO: different overload for ExtendedDayNum
+ const LUTIndex i = toLUTIndex(v);
+ return i + 1 - toFirstDayNumOfYearIndex(i);
+ }
/// Number of week from some fixed moment in the past. Week begins at monday.
/// (round down to monday and divide DayNum by 7; we made an assumption,
/// that in domain of the function there was no weeks with any other number of days than 7)
- inline unsigned toRelativeWeekNum(DayNum d) const
+ template
+ inline unsigned toRelativeWeekNum(DateOrTime v) const
{
+ const LUTIndex i = toLUTIndex(v);
/// We add 8 to avoid underflow at beginning of unix epoch.
- return (d + 8 - toDayOfWeek(d)) / 7;
- }
-
- inline unsigned toRelativeWeekNum(time_t t) const
- {
- return toRelativeWeekNum(toDayNum(t));
+ return toDayNum(i + 8 - toDayOfWeek(i)) / 7;
}
/// Get year that contains most of the current week. Week begins at monday.
- inline unsigned toISOYear(DayNum d) const
+ template
+ inline unsigned toISOYear(DateOrTime v) const
{
+ const LUTIndex i = toLUTIndex(v);
/// That's effectively the year of thursday of current week.
- return toYear(DayNum(d + 4 - toDayOfWeek(d)));
- }
-
- inline unsigned toISOYear(time_t t) const
- {
- return toISOYear(toDayNum(t));
+ return toYear(toLUTIndex(i + 4 - toDayOfWeek(i)));
}
/// ISO year begins with a monday of the week that is contained more than by half in the corresponding calendar year.
/// Example: ISO year 2019 begins at 2018-12-31. And ISO year 2017 begins at 2017-01-02.
/// https://en.wikipedia.org/wiki/ISO_week_date
- inline DayNum toFirstDayNumOfISOYear(DayNum d) const
+ template
+ inline LUTIndex toFirstDayNumOfISOYearIndex(DateOrTime v) const
{
- auto iso_year = toISOYear(d);
+ const LUTIndex i = toLUTIndex(v);
+ auto iso_year = toISOYear(i);
- DayNum first_day_of_year = years_lut[iso_year - DATE_LUT_MIN_YEAR];
+ const auto first_day_of_year = years_lut[iso_year - DATE_LUT_MIN_YEAR];
auto first_day_of_week_of_year = lut[first_day_of_year].day_of_week;
- return DayNum(first_day_of_week_of_year <= 4
+ return LUTIndex{first_day_of_week_of_year <= 4
? first_day_of_year + 1 - first_day_of_week_of_year
- : first_day_of_year + 8 - first_day_of_week_of_year);
+ : first_day_of_year + 8 - first_day_of_week_of_year};
}
- inline DayNum toFirstDayNumOfISOYear(time_t t) const
+ template
+ inline ExtendedDayNum toFirstDayNumOfISOYear(DateOrTime v) const
{
- return toFirstDayNumOfISOYear(toDayNum(t));
+ return toDayNum(toFirstDayNumOfISOYearIndex(v));
}
inline time_t toFirstDayOfISOYear(time_t t) const
{
- return fromDayNum(toFirstDayNumOfISOYear(t));
+ return lut[toFirstDayNumOfISOYearIndex(t)].date;
}
/// ISO 8601 week number. Week begins at monday.
/// The week number 1 is the first week in year that contains 4 or more days (that's more than half).
- inline unsigned toISOWeek(DayNum d) const
+ template
+ inline unsigned toISOWeek(DateOrTime v) const
{
- return 1 + DayNum(toFirstDayNumOfWeek(d) - toFirstDayNumOfISOYear(d)) / 7;
- }
-
- inline unsigned toISOWeek(time_t t) const
- {
- return toISOWeek(toDayNum(t));
+ return 1 + (toFirstDayNumOfWeek(v) - toFirstDayNumOfISOYear(v)) / 7;
}
/*
@@ -463,30 +624,33 @@ public:
Otherwise it is the last week of the previous year, and the
next week is week 1.
*/
- inline YearWeek toYearWeek(DayNum d, UInt8 week_mode) const
+ template
+ inline YearWeek toYearWeek(DateOrTime v, UInt8 week_mode) const
{
- bool newyear_day_mode = week_mode & static_cast(WeekModeFlag::NEWYEAR_DAY);
+ const bool newyear_day_mode = week_mode & static_cast(WeekModeFlag::NEWYEAR_DAY);
week_mode = check_week_mode(week_mode);
- bool monday_first_mode = week_mode & static_cast(WeekModeFlag::MONDAY_FIRST);
+ const bool monday_first_mode = week_mode & static_cast(WeekModeFlag::MONDAY_FIRST);
bool week_year_mode = week_mode & static_cast(WeekModeFlag::YEAR);
- bool first_weekday_mode = week_mode & static_cast(WeekModeFlag::FIRST_WEEKDAY);
+ const bool first_weekday_mode = week_mode & static_cast(WeekModeFlag::FIRST_WEEKDAY);
+
+ const LUTIndex i = toLUTIndex(v);
// Calculate week number of WeekModeFlag::NEWYEAR_DAY mode
if (newyear_day_mode)
{
- return toYearWeekOfNewyearMode(d, monday_first_mode);
+ return toYearWeekOfNewyearMode(i, monday_first_mode);
}
- YearWeek yw(toYear(d), 0);
+ YearWeek yw(toYear(i), 0);
UInt16 days = 0;
- UInt16 daynr = makeDayNum(yw.first, toMonth(d), toDayOfMonth(d));
- UInt16 first_daynr = makeDayNum(yw.first, 1, 1);
+ const auto daynr = makeDayNum(yw.first, toMonth(i), toDayOfMonth(i));
+ auto first_daynr = makeDayNum(yw.first, 1, 1);
// 0 for monday, 1 for tuesday ...
// get weekday from first day in year.
- UInt16 weekday = calc_weekday(DayNum(first_daynr), !monday_first_mode);
+ UInt16 weekday = calc_weekday(first_daynr, !monday_first_mode);
- if (toMonth(d) == 1 && toDayOfMonth(d) <= static_cast(7 - weekday))
+ if (toMonth(i) == 1 && toDayOfMonth(i) <= static_cast(7 - weekday))
{
if (!week_year_mode && ((first_weekday_mode && weekday != 0) || (!first_weekday_mode && weekday >= 4)))
return yw;
@@ -517,48 +681,51 @@ public:
/// Calculate week number of WeekModeFlag::NEWYEAR_DAY mode
/// The week number 1 is the first week in year that contains January 1,
- inline YearWeek toYearWeekOfNewyearMode(DayNum d, bool monday_first_mode) const
+ template
+ inline YearWeek toYearWeekOfNewyearMode(DateOrTime v, bool monday_first_mode) const
{
YearWeek yw(0, 0);
UInt16 offset_day = monday_first_mode ? 0U : 1U;
+ const LUTIndex i = LUTIndex(v);
+
// Checking the week across the year
- yw.first = toYear(DayNum(d + 7 - toDayOfWeek(DayNum(d + offset_day))));
+ yw.first = toYear(i + 7 - toDayOfWeek(i + offset_day));
- DayNum first_day = makeDayNum(yw.first, 1, 1);
- DayNum this_day = d;
+ auto first_day = makeLUTIndex(yw.first, 1, 1);
+ auto this_day = i;
+ // TODO: do not perform calculations in terms of DayNum, since that would under/overflow for extended range.
if (monday_first_mode)
{
// Rounds down a date to the nearest Monday.
first_day = toFirstDayNumOfWeek(first_day);
- this_day = toFirstDayNumOfWeek(d);
+ this_day = toFirstDayNumOfWeek(i);
}
else
{
// Rounds down a date to the nearest Sunday.
if (toDayOfWeek(first_day) != 7)
- first_day = DayNum(first_day - toDayOfWeek(first_day));
- if (toDayOfWeek(d) != 7)
- this_day = DayNum(d - toDayOfWeek(d));
+ first_day = ExtendedDayNum(first_day - toDayOfWeek(first_day));
+ if (toDayOfWeek(i) != 7)
+ this_day = ExtendedDayNum(i - toDayOfWeek(i));
}
yw.second = (this_day - first_day) / 7 + 1;
return yw;
}
- /**
- * get first day of week with week_mode, return Sunday or Monday
- */
- inline DayNum toFirstDayNumOfWeek(DayNum d, UInt8 week_mode) const
+ /// Get first day of week with week_mode, return Sunday or Monday
+ template
+ inline ExtendedDayNum toFirstDayNumOfWeek(DateOrTime v, UInt8 week_mode) const
{
bool monday_first_mode = week_mode & static_cast(WeekModeFlag::MONDAY_FIRST);
if (monday_first_mode)
{
- return toFirstDayNumOfWeek(d);
+ return toFirstDayNumOfWeek(v);
}
else
{
- return (toDayOfWeek(d) != 7) ? DayNum(d - toDayOfWeek(d)) : d;
+ return (toDayOfWeek(v) != 7) ? ExtendedDayNum(v - toDayOfWeek(v)) : toDayNum(v);
}
}
@@ -574,192 +741,231 @@ public:
/** Calculate weekday from d.
* Returns 0 for monday, 1 for tuesday...
*/
- inline unsigned calc_weekday(DayNum d, bool sunday_first_day_of_week) const
+ template
+ inline unsigned calc_weekday(DateOrTime v, bool sunday_first_day_of_week) const
{
+ const LUTIndex i = toLUTIndex(v);
if (!sunday_first_day_of_week)
- return toDayOfWeek(d) - 1;
+ return toDayOfWeek(i) - 1;
else
- return toDayOfWeek(DayNum(d + 1)) - 1;
+ return toDayOfWeek(i + 1) - 1;
}
/// Calculate days in one year.
- inline unsigned calc_days_in_year(UInt16 year) const
+ inline unsigned calc_days_in_year(Int32 year) const
{
return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)) ? 366 : 365);
}
/// Number of month from some fixed moment in the past (year * 12 + month)
- inline unsigned toRelativeMonthNum(DayNum d) const
+ template
+ inline unsigned toRelativeMonthNum(DateOrTime v) const
{
- return lut[d].year * 12 + lut[d].month;
+ const LUTIndex i = toLUTIndex(v);
+ return lut[i].year * 12 + lut[i].month;
}
- inline unsigned toRelativeMonthNum(time_t t) const
+ template
+ inline unsigned toRelativeQuarterNum(DateOrTime v) const
{
- return toRelativeMonthNum(toDayNum(t));
- }
-
- inline unsigned toRelativeQuarterNum(DayNum d) const
- {
- return lut[d].year * 4 + (lut[d].month - 1) / 3;
- }
-
- inline unsigned toRelativeQuarterNum(time_t t) const
- {
- return toRelativeQuarterNum(toDayNum(t));
+ const LUTIndex i = toLUTIndex(v);
+ return lut[i].year * 4 + (lut[i].month - 1) / 3;
}
/// We count all hour-length intervals, unrelated to offset changes.
inline time_t toRelativeHourNum(time_t t) const
{
- if (offset_is_whole_number_of_hours_everytime)
+ if (t >= 0 && offset_is_whole_number_of_hours_during_epoch)
return t / 3600;
/// Assume that if offset was fractional, then the fraction is the same as at the beginning of epoch.
/// NOTE This assumption is false for "Pacific/Pitcairn" and "Pacific/Kiritimati" time zones.
- return (t + 86400 - offset_at_start_of_epoch) / 3600;
+ return (t + DATE_LUT_ADD + 86400 - offset_at_start_of_epoch) / 3600 - (DATE_LUT_ADD / 3600);
}
- inline time_t toRelativeHourNum(DayNum d) const
+ template
+ inline time_t toRelativeHourNum(DateOrTime v) const
{
- return toRelativeHourNum(lut[d].date);
+ return toRelativeHourNum(lut[toLUTIndex(v)].date);
}
inline time_t toRelativeMinuteNum(time_t t) const
{
- return t / 60;
+ return (t + DATE_LUT_ADD) / 60 - (DATE_LUT_ADD / 60);
}
- inline time_t toRelativeMinuteNum(DayNum d) const
+ template
+ inline time_t toRelativeMinuteNum(DateOrTime v) const
{
- return toRelativeMinuteNum(lut[d].date);
+ return toRelativeMinuteNum(lut[toLUTIndex(v)].date);
}
- inline DayNum toStartOfYearInterval(DayNum d, UInt64 years) const
+ template
+ inline ExtendedDayNum toStartOfYearInterval(DateOrTime v, UInt64 years) const
{
if (years == 1)
- return toFirstDayNumOfYear(d);
- return years_lut[(lut[d].year - DATE_LUT_MIN_YEAR) / years * years];
+ return toFirstDayNumOfYear(v);
+
+ const LUTIndex i = toLUTIndex(v);
+
+ UInt16 year = lut[i].year / years * years;
+
+ /// For example, rounding down 1925 to 100 years will be 1900, but it's less than min supported year.
+ if (unlikely(year < DATE_LUT_MIN_YEAR))
+ year = DATE_LUT_MIN_YEAR;
+
+ return toDayNum(years_lut[year - DATE_LUT_MIN_YEAR]);
}
- inline DayNum toStartOfQuarterInterval(DayNum d, UInt64 quarters) const
+ inline ExtendedDayNum toStartOfQuarterInterval(ExtendedDayNum d, UInt64 quarters) const
{
if (quarters == 1)
return toFirstDayNumOfQuarter(d);
return toStartOfMonthInterval(d, quarters * 3);
}
- inline DayNum toStartOfMonthInterval(DayNum d, UInt64 months) const
+ inline ExtendedDayNum toStartOfMonthInterval(ExtendedDayNum d, UInt64 months) const
{
if (months == 1)
return toFirstDayNumOfMonth(d);
- const auto & date = lut[d];
- UInt32 month_total_index = (date.year - DATE_LUT_MIN_YEAR) * 12 + date.month - 1;
- return years_months_lut[month_total_index / months * months];
+ const Values & values = lut[toLUTIndex(d)];
+ UInt32 month_total_index = (values.year - DATE_LUT_MIN_YEAR) * 12 + values.month - 1;
+ return toDayNum(years_months_lut[month_total_index / months * months]);
}
- inline DayNum toStartOfWeekInterval(DayNum d, UInt64 weeks) const
+ inline ExtendedDayNum toStartOfWeekInterval(ExtendedDayNum d, UInt64 weeks) const
{
if (weeks == 1)
return toFirstDayNumOfWeek(d);
UInt64 days = weeks * 7;
// January 1st 1970 was Thursday so we need this 4-days offset to make weeks start on Monday.
- return DayNum(4 + (d - 4) / days * days);
+ return ExtendedDayNum(4 + (d - 4) / days * days);
}
- inline time_t toStartOfDayInterval(DayNum d, UInt64 days) const
+ inline time_t toStartOfDayInterval(ExtendedDayNum d, UInt64 days) const
{
if (days == 1)
return toDate(d);
- return lut[d / days * days].date;
+ return lut[toLUTIndex(ExtendedDayNum(d / days * days))].date;
}
inline time_t toStartOfHourInterval(time_t t, UInt64 hours) const
{
if (hours == 1)
return toStartOfHour(t);
+
+ /** We will round the hour number since the midnight.
+ * It may split the day into non-equal intervals.
+ * For example, if we will round to 11-hour interval,
+ * the day will be split to the intervals 00:00:00..10:59:59, 11:00:00..21:59:59, 22:00:00..23:59:59.
+ * In case of daylight saving time or other transitions,
+ * the intervals can be shortened or prolonged to the amount of transition.
+ */
+
UInt64 seconds = hours * 3600;
- t = t / seconds * seconds;
- if (offset_is_whole_number_of_hours_everytime)
- return t;
- return toStartOfHour(t);
+
+ const LUTIndex index = findIndex(t);
+ const Values & values = lut[index];
+
+ time_t time = t - values.date;
+ if (time >= values.time_at_offset_change())
+ {
+ /// Align to new hour numbers before rounding.
+ time += values.amount_of_offset_change();
+ time = time / seconds * seconds;
+
+ /// Should subtract the shift back but only if rounded time is not before shift.
+ if (time >= values.time_at_offset_change())
+ {
+ time -= values.amount_of_offset_change();
+
+ /// With cutoff at the time of the shift. Otherwise we may end up with something like 23:00 previous day.
+ if (time < values.time_at_offset_change())
+ time = values.time_at_offset_change();
+ }
+ }
+ else
+ {
+ time = time / seconds * seconds;
+ }
+
+ return values.date + time;
}
inline time_t toStartOfMinuteInterval(time_t t, UInt64 minutes) const
{
if (minutes == 1)
return toStartOfMinute(t);
+
+ /** In contrast to "toStartOfHourInterval" function above,
+ * the minute intervals are not aligned to the midnight.
+ * You will get unexpected results if for example, you round down to 60 minute interval
+ * and there was a time shift to 30 minutes.
+ *
+ * But this is not specified in docs and can be changed in future.
+ */
+
UInt64 seconds = 60 * minutes;
- return t / seconds * seconds;
+ return roundDown(t, seconds);
}
inline time_t toStartOfSecondInterval(time_t t, UInt64 seconds) const
{
if (seconds == 1)
return t;
- return t / seconds * seconds;
+
+ return roundDown(t, seconds);
+ }
+
+ inline LUTIndex makeLUTIndex(Int16 year, UInt8 month, UInt8 day_of_month) const
+ {
+ if (unlikely(year < DATE_LUT_MIN_YEAR || year > DATE_LUT_MAX_YEAR || month < 1 || month > 12 || day_of_month < 1 || day_of_month > 31))
+ return LUTIndex(0);
+
+ return LUTIndex{years_months_lut[(year - DATE_LUT_MIN_YEAR) * 12 + month - 1] + day_of_month - 1};
}
/// Create DayNum from year, month, day of month.
- inline DayNum makeDayNum(UInt16 year, UInt8 month, UInt8 day_of_month) const
+ inline ExtendedDayNum makeDayNum(Int16 year, UInt8 month, UInt8 day_of_month) const
{
if (unlikely(year < DATE_LUT_MIN_YEAR || year > DATE_LUT_MAX_YEAR || month < 1 || month > 12 || day_of_month < 1 || day_of_month > 31))
- return DayNum(0); // TODO (nemkov, DateTime64 phase 2): implement creating real date for year outside of LUT range.
+ return ExtendedDayNum(0);
- // The day after 2106-02-07 will not stored fully as struct Values, so just overflow it as 0
- if (unlikely(year == DATE_LUT_MAX_YEAR && (month > 2 || (month == 2 && day_of_month > 7))))
- return DayNum(0);
-
- return DayNum(years_months_lut[(year - DATE_LUT_MIN_YEAR) * 12 + month - 1] + day_of_month - 1);
+ return toDayNum(makeLUTIndex(year, month, day_of_month));
}
- inline time_t makeDate(UInt16 year, UInt8 month, UInt8 day_of_month) const
+ inline time_t makeDate(Int16 year, UInt8 month, UInt8 day_of_month) const
{
- return lut[makeDayNum(year, month, day_of_month)].date;
+ return lut[makeLUTIndex(year, month, day_of_month)].date;
}
/** Does not accept daylight saving time as argument: in case of ambiguity, it choose greater timestamp.
*/
- inline time_t makeDateTime(UInt16 year, UInt8 month, UInt8 day_of_month, UInt8 hour, UInt8 minute, UInt8 second) const
+ inline time_t makeDateTime(Int16 year, UInt8 month, UInt8 day_of_month, UInt8 hour, UInt8 minute, UInt8 second) const
{
- size_t index = makeDayNum(year, month, day_of_month);
+ size_t index = makeLUTIndex(year, month, day_of_month);
UInt32 time_offset = hour * 3600 + minute * 60 + second;
- if (time_offset >= lut[index].time_at_offset_change)
- time_offset -= lut[index].amount_of_offset_change;
+ if (time_offset >= lut[index].time_at_offset_change())
+ time_offset -= lut[index].amount_of_offset_change();
- UInt32 res = lut[index].date + time_offset;
-
- if (unlikely(res > DATE_LUT_MAX))
- return 0;
-
- return res;
+ return lut[index].date + time_offset;
}
- inline const Values & getValues(DayNum d) const { return lut[d]; }
- inline const Values & getValues(time_t t) const { return lut[findIndex(t)]; }
+ template
+ inline const Values & getValues(DateOrTime v) const { return lut[toLUTIndex(v)]; }
- inline UInt32 toNumYYYYMM(time_t t) const
+ template
+ inline UInt32 toNumYYYYMM(DateOrTime v) const
{
- const Values & values = find(t);
+ const Values & values = getValues(v);
return values.year * 100 + values.month;
}
- inline UInt32 toNumYYYYMM(DayNum d) const
+ template
+ inline UInt32 toNumYYYYMMDD(DateOrTime v) const
{
- const Values & values = lut[d];
- return values.year * 100 + values.month;
- }
-
- inline UInt32 toNumYYYYMMDD(time_t t) const
- {
- const Values & values = find(t);
- return values.year * 10000 + values.month * 100 + values.day_of_month;
- }
-
- inline UInt32 toNumYYYYMMDD(DayNum d) const
- {
- const Values & values = lut[d];
+ const Values & values = getValues(v);
return values.year * 10000 + values.month * 100 + values.day_of_month;
}
@@ -768,22 +974,85 @@ public:
return makeDate(num / 10000, num / 100 % 100, num % 100);
}
- inline DayNum YYYYMMDDToDayNum(UInt32 num) const
+ inline ExtendedDayNum YYYYMMDDToDayNum(UInt32 num) const
{
return makeDayNum(num / 10000, num / 100 % 100, num % 100);
}
+ struct DateComponents
+ {
+ uint16_t year;
+ uint8_t month;
+ uint8_t day;
+ };
+
+ struct TimeComponents
+ {
+ uint8_t hour;
+ uint8_t minute;
+ uint8_t second;
+ };
+
+ struct DateTimeComponents
+ {
+ DateComponents date;
+ TimeComponents time;
+ };
+
+ inline DateComponents toDateComponents(time_t t) const
+ {
+ const Values & values = getValues(t);
+ return { values.year, values.month, values.day_of_month };
+ }
+
+ inline DateTimeComponents toDateTimeComponents(time_t t) const
+ {
+ const LUTIndex index = findIndex(t);
+ const Values & values = lut[index];
+
+ DateTimeComponents res;
+
+ res.date.year = values.year;
+ res.date.month = values.month;
+ res.date.day = values.day_of_month;
+
+ time_t time = t - values.date;
+ if (time >= values.time_at_offset_change())
+ time += values.amount_of_offset_change();
+
+ if (unlikely(time < 0))
+ {
+ res.time.second = 0;
+ res.time.minute = 0;
+ res.time.hour = 0;
+ }
+ else
+ {
+ res.time.second = time % 60;
+ res.time.minute = time / 60 % 60;
+ res.time.hour = time / 3600;
+ }
+
+ /// In case time was changed backwards at the start of next day, we will repeat the hour 23.
+ if (unlikely(res.time.hour > 23))
+ res.time.hour = 23;
+
+ return res;
+ }
+
+
inline UInt64 toNumYYYYMMDDhhmmss(time_t t) const
{
- const Values & values = find(t);
+ DateTimeComponents components = toDateTimeComponents(t);
+
return
- toSecond(t)
- + toMinute(t) * 100
- + toHour(t) * 10000
- + UInt64(values.day_of_month) * 1000000
- + UInt64(values.month) * 100000000
- + UInt64(values.year) * 10000000000;
+ components.time.second
+ + components.time.minute * 100
+ + components.time.hour * 10000
+ + UInt64(components.date.day) * 1000000
+ + UInt64(components.date.month) * 100000000
+ + UInt64(components.date.year) * 10000000000;
}
inline time_t YYYYMMDDhhmmssToTime(UInt64 num) const
@@ -802,15 +1071,19 @@ public:
inline NO_SANITIZE_UNDEFINED time_t addDays(time_t t, Int64 delta) const
{
- DayNum index = findIndex(t);
- time_t time_offset = toHour(t) * 3600 + toMinute(t) * 60 + toSecond(t);
+ const LUTIndex index = findIndex(t);
+ const Values & values = lut[index];
- index += delta;
+ time_t time = t - values.date;
+ if (time >= values.time_at_offset_change())
+ time += values.amount_of_offset_change();
- if (time_offset >= lut[index].time_at_offset_change)
- time_offset -= lut[index].amount_of_offset_change;
+ const LUTIndex new_index = index + delta;
- return lut[index].date + time_offset;
+ if (time >= lut[new_index].time_at_offset_change())
+ time -= lut[new_index].amount_of_offset_change();
+
+ return lut[new_index].date + time;
}
inline NO_SANITIZE_UNDEFINED time_t addWeeks(time_t t, Int64 delta) const
@@ -818,7 +1091,7 @@ public:
return addDays(t, delta * 7);
}
- inline UInt8 saturateDayOfMonth(UInt16 year, UInt8 month, UInt8 day_of_month) const
+ inline UInt8 saturateDayOfMonth(Int16 year, UInt8 month, UInt8 day_of_month) const
{
if (likely(day_of_month <= 28))
return day_of_month;
@@ -831,25 +1104,12 @@ public:
return day_of_month;
}
- /// If resulting month has less deys than source month, then saturation can happen.
- /// Example: 31 Aug + 1 month = 30 Sep.
- inline time_t addMonths(time_t t, Int64 delta) const
+ template
+ inline LUTIndex NO_SANITIZE_UNDEFINED addMonthsIndex(DateOrTime v, Int64 delta) const
{
- DayNum result_day = addMonths(toDayNum(t), delta);
+ const Values & values = lut[toLUTIndex(v)];
- time_t time_offset = toHour(t) * 3600 + toMinute(t) * 60 + toSecond(t);
-
- if (time_offset >= lut[result_day].time_at_offset_change)
- time_offset -= lut[result_day].amount_of_offset_change;
-
- return lut[result_day].date + time_offset;
- }
-
- inline NO_SANITIZE_UNDEFINED DayNum addMonths(DayNum d, Int64 delta) const
- {
- const Values & values = lut[d];
-
- Int64 month = static_cast(values.month) + delta;
+ Int64 month = values.month + delta;
if (month > 0)
{
@@ -857,7 +1117,7 @@ public:
month = ((month - 1) % 12) + 1;
auto day_of_month = saturateDayOfMonth(year, month, values.day_of_month);
- return makeDayNum(year, month, day_of_month);
+ return makeLUTIndex(year, month, day_of_month);
}
else
{
@@ -865,36 +1125,48 @@ public:
month = 12 - (-month % 12);
auto day_of_month = saturateDayOfMonth(year, month, values.day_of_month);
- return makeDayNum(year, month, day_of_month);
+ return makeLUTIndex(year, month, day_of_month);
}
}
- inline NO_SANITIZE_UNDEFINED time_t addQuarters(time_t t, Int64 delta) const
+ /// If resulting month has less deys than source month, then saturation can happen.
+ /// Example: 31 Aug + 1 month = 30 Sep.
+ inline time_t NO_SANITIZE_UNDEFINED addMonths(time_t t, Int64 delta) const
+ {
+ const auto result_day = addMonthsIndex(t, delta);
+
+ const LUTIndex index = findIndex(t);
+ const Values & values = lut[index];
+
+ time_t time = t - values.date;
+ if (time >= values.time_at_offset_change())
+ time += values.amount_of_offset_change();
+
+ if (time >= lut[result_day].time_at_offset_change())
+ time -= lut[result_day].amount_of_offset_change();
+
+ return lut[result_day].date + time;
+ }
+
+ inline ExtendedDayNum NO_SANITIZE_UNDEFINED addMonths(ExtendedDayNum d, Int64 delta) const
+ {
+ return toDayNum(addMonthsIndex(d, delta));
+ }
+
+ inline time_t NO_SANITIZE_UNDEFINED addQuarters(time_t t, Int64 delta) const
{
return addMonths(t, delta * 3);
}
- inline NO_SANITIZE_UNDEFINED DayNum addQuarters(DayNum d, Int64 delta) const
+ inline ExtendedDayNum addQuarters(ExtendedDayNum d, Int64 delta) const
{
return addMonths(d, delta * 3);
}
- /// Saturation can occur if 29 Feb is mapped to non-leap year.
- inline NO_SANITIZE_UNDEFINED time_t addYears(time_t t, Int64 delta) const
+ template
+ inline LUTIndex NO_SANITIZE_UNDEFINED addYearsIndex(DateOrTime v, Int64 delta) const
{
- DayNum result_day = addYears(toDayNum(t), delta);
-
- time_t time_offset = toHour(t) * 3600 + toMinute(t) * 60 + toSecond(t);
-
- if (time_offset >= lut[result_day].time_at_offset_change)
- time_offset -= lut[result_day].amount_of_offset_change;
-
- return lut[result_day].date + time_offset;
- }
-
- inline NO_SANITIZE_UNDEFINED DayNum addYears(DayNum d, Int64 delta) const
- {
- const Values & values = lut[d];
+ const Values & values = lut[toLUTIndex(v)];
auto year = values.year + delta;
auto month = values.month;
@@ -904,42 +1176,61 @@ public:
if (unlikely(day_of_month == 29 && month == 2))
day_of_month = saturateDayOfMonth(year, month, day_of_month);
- return makeDayNum(year, month, day_of_month);
+ return makeLUTIndex(year, month, day_of_month);
+ }
+
+ /// Saturation can occur if 29 Feb is mapped to non-leap year.
+ inline time_t addYears(time_t t, Int64 delta) const
+ {
+ auto result_day = addYearsIndex(t, delta);
+
+ const LUTIndex index = findIndex(t);
+ const Values & values = lut[index];
+
+ time_t time = t - values.date;
+ if (time >= values.time_at_offset_change())
+ time += values.amount_of_offset_change();
+
+ if (time >= lut[result_day].time_at_offset_change())
+ time -= lut[result_day].amount_of_offset_change();
+
+ return lut[result_day].date + time;
+ }
+
+ inline ExtendedDayNum addYears(ExtendedDayNum d, Int64 delta) const
+ {
+ return toDayNum(addYearsIndex(d, delta));
}
inline std::string timeToString(time_t t) const
{
- const Values & values = find(t);
+ DateTimeComponents components = toDateTimeComponents(t);
std::string s {"0000-00-00 00:00:00"};
- s[0] += values.year / 1000;
- s[1] += (values.year / 100) % 10;
- s[2] += (values.year / 10) % 10;
- s[3] += values.year % 10;
- s[5] += values.month / 10;
- s[6] += values.month % 10;
- s[8] += values.day_of_month / 10;
- s[9] += values.day_of_month % 10;
+ s[0] += components.date.year / 1000;
+ s[1] += (components.date.year / 100) % 10;
+ s[2] += (components.date.year / 10) % 10;
+ s[3] += components.date.year % 10;
+ s[5] += components.date.month / 10;
+ s[6] += components.date.month % 10;
+ s[8] += components.date.day / 10;
+ s[9] += components.date.day % 10;
- auto hour = toHour(t);
- auto minute = toMinute(t);
- auto second = toSecond(t);
-
- s[11] += hour / 10;
- s[12] += hour % 10;
- s[14] += minute / 10;
- s[15] += minute % 10;
- s[17] += second / 10;
- s[18] += second % 10;
+ s[11] += components.time.hour / 10;
+ s[12] += components.time.hour % 10;
+ s[14] += components.time.minute / 10;
+ s[15] += components.time.minute % 10;
+ s[17] += components.time.second / 10;
+ s[18] += components.time.second % 10;
return s;
}
inline std::string dateToString(time_t t) const
{
- const Values & values = find(t);
+ const Values & values = getValues(t);
std::string s {"0000-00-00"};
@@ -955,9 +1246,9 @@ public:
return s;
}
- inline std::string dateToString(DayNum d) const
+ inline std::string dateToString(ExtendedDayNum d) const
{
- const Values & values = lut[d];
+ const Values & values = getValues(d);
std::string s {"0000-00-00"};
diff --git a/base/common/DayNum.h b/base/common/DayNum.h
index a4ef0c43b69..5cf4d4635c8 100644
--- a/base/common/DayNum.h
+++ b/base/common/DayNum.h
@@ -7,3 +7,8 @@
* See DateLUTImpl for usage examples.
*/
STRONG_TYPEDEF(UInt16, DayNum)
+
+/** Represent number of days since 1970-01-01 but in extended range,
+ * for dates before 1970-01-01 and after 2105
+ */
+STRONG_TYPEDEF(Int32, ExtendedDayNum)
diff --git a/base/common/LocalDate.h b/base/common/LocalDate.h
index e5ebe877bc5..b1e6eeb907c 100644
--- a/base/common/LocalDate.h
+++ b/base/common/LocalDate.h
@@ -92,20 +92,10 @@ public:
LocalDate(const LocalDate &) noexcept = default;
LocalDate & operator= (const LocalDate &) noexcept = default;
- LocalDate & operator= (time_t time)
- {
- init(time);
- return *this;
- }
-
- operator time_t() const
- {
- return DateLUT::instance().makeDate(m_year, m_month, m_day);
- }
-
DayNum getDayNum() const
{
- return DateLUT::instance().makeDayNum(m_year, m_month, m_day);
+ const auto & lut = DateLUT::instance();
+ return DayNum(lut.makeDayNum(m_year, m_month, m_day).toUnderType());
}
operator DayNum() const
@@ -166,12 +156,3 @@ public:
};
static_assert(sizeof(LocalDate) == 4);
-
-
-namespace std
-{
-inline string to_string(const LocalDate & date)
-{
- return date.toString();
-}
-}
diff --git a/base/common/LocalDateTime.h b/base/common/LocalDateTime.h
index 0e237789bd1..dde283e5ebb 100644
--- a/base/common/LocalDateTime.h
+++ b/base/common/LocalDateTime.h
@@ -29,29 +29,16 @@ private:
/// NOTE We may use attribute packed instead, but it is less portable.
unsigned char pad = 0;
- void init(time_t time)
+ void init(time_t time, const DateLUTImpl & time_zone)
{
- if (unlikely(time > DATE_LUT_MAX || time == 0))
- {
- m_year = 0;
- m_month = 0;
- m_day = 0;
- m_hour = 0;
- m_minute = 0;
- m_second = 0;
+ DateLUTImpl::DateTimeComponents components = time_zone.toDateTimeComponents(time);
- return;
- }
-
- const auto & date_lut = DateLUT::instance();
- const auto & values = date_lut.getValues(time);
-
- m_year = values.year;
- m_month = values.month;
- m_day = values.day_of_month;
- m_hour = date_lut.toHour(time);
- m_minute = date_lut.toMinute(time);
- m_second = date_lut.toSecond(time);
+ m_year = components.date.year;
+ m_month = components.date.month;
+ m_day = components.date.day;
+ m_hour = components.time.hour;
+ m_minute = components.time.minute;
+ m_second = components.time.second;
(void)pad; /// Suppress unused private field warning.
}
@@ -73,9 +60,9 @@ private:
}
public:
- explicit LocalDateTime(time_t time)
+ explicit LocalDateTime(time_t time, const DateLUTImpl & time_zone = DateLUT::instance())
{
- init(time);
+ init(time, time_zone);
}
LocalDateTime(unsigned short year_, unsigned char month_, unsigned char day_,
@@ -104,19 +91,6 @@ public:
LocalDateTime(const LocalDateTime &) noexcept = default;
LocalDateTime & operator= (const LocalDateTime &) noexcept = default;
- LocalDateTime & operator= (time_t time)
- {
- init(time);
- return *this;
- }
-
- operator time_t() const
- {
- return m_year == 0
- ? 0
- : DateLUT::instance().makeDateTime(m_year, m_month, m_day, m_hour, m_minute, m_second);
- }
-
unsigned short year() const { return m_year; }
unsigned char month() const { return m_month; }
unsigned char day() const { return m_day; }
@@ -132,8 +106,30 @@ public:
void second(unsigned char x) { m_second = x; }
LocalDate toDate() const { return LocalDate(m_year, m_month, m_day); }
+ LocalDateTime toStartOfDate() const { return LocalDateTime(m_year, m_month, m_day, 0, 0, 0); }
- LocalDateTime toStartOfDate() { return LocalDateTime(m_year, m_month, m_day, 0, 0, 0); }
+ std::string toString() const
+ {
+ std::string s{"0000-00-00 00:00:00"};
+
+ s[0] += m_year / 1000;
+ s[1] += (m_year / 100) % 10;
+ s[2] += (m_year / 10) % 10;
+ s[3] += m_year % 10;
+ s[5] += m_month / 10;
+ s[6] += m_month % 10;
+ s[8] += m_day / 10;
+ s[9] += m_day % 10;
+
+ s[11] += m_hour / 10;
+ s[12] += m_hour % 10;
+ s[14] += m_minute / 10;
+ s[15] += m_minute % 10;
+ s[17] += m_second / 10;
+ s[18] += m_second % 10;
+
+ return s;
+ }
bool operator< (const LocalDateTime & other) const
{
@@ -167,14 +163,3 @@ public:
};
static_assert(sizeof(LocalDateTime) == 8);
-
-
-namespace std
-{
-inline string to_string(const LocalDateTime & datetime)
-{
- stringstream str;
- str << datetime;
- return str.str();
-}
-}
diff --git a/src/Common/MoveOrCopyIfThrow.h b/base/common/MoveOrCopyIfThrow.h
similarity index 100%
rename from src/Common/MoveOrCopyIfThrow.h
rename to base/common/MoveOrCopyIfThrow.h
diff --git a/base/common/arithmeticOverflow.h b/base/common/arithmeticOverflow.h
index a92fe56b9cb..c170d214636 100644
--- a/base/common/arithmeticOverflow.h
+++ b/base/common/arithmeticOverflow.h
@@ -25,6 +25,12 @@ namespace common
return x - y;
}
+ template
+ inline auto NO_SANITIZE_UNDEFINED negateIgnoreOverflow(T x)
+ {
+ return -x;
+ }
+
template
inline bool addOverflow(T x, T y, T & res)
{
diff --git a/base/common/setTerminalEcho.cpp b/base/common/setTerminalEcho.cpp
index 658f27705ba..66db216a235 100644
--- a/base/common/setTerminalEcho.cpp
+++ b/base/common/setTerminalEcho.cpp
@@ -1,45 +1,28 @@
-// https://stackoverflow.com/questions/1413445/reading-a-password-from-stdcin
-
#include
#include
#include
#include
#include
-
-#ifdef WIN32
-#include
-#else
#include
#include
-#include
-#endif
+
void setTerminalEcho(bool enable)
{
-#ifdef WIN32
- auto handle = GetStdHandle(STD_INPUT_HANDLE);
- DWORD mode;
- if (!GetConsoleMode(handle, &mode))
- throw std::runtime_error(std::string("setTerminalEcho failed get: ") + std::to_string(GetLastError()));
+ /// Obtain terminal attributes,
+ /// toggle the ECHO flag
+ /// and set them back.
- if (!enable)
- mode &= ~ENABLE_ECHO_INPUT;
- else
- mode |= ENABLE_ECHO_INPUT;
+ struct termios tty{};
- if (!SetConsoleMode(handle, mode))
- throw std::runtime_error(std::string("setTerminalEcho failed set: ") + std::to_string(GetLastError()));
-#else
- struct termios tty;
- if (tcgetattr(STDIN_FILENO, &tty))
+ if (0 != tcgetattr(STDIN_FILENO, &tty))
throw std::runtime_error(std::string("setTerminalEcho failed get: ") + errnoToString(errno));
- if (!enable)
- tty.c_lflag &= ~ECHO;
- else
- tty.c_lflag |= ECHO;
- auto ret = tcsetattr(STDIN_FILENO, TCSANOW, &tty);
- if (ret)
+ if (enable)
+ tty.c_lflag |= ECHO;
+ else
+ tty.c_lflag &= ~ECHO;
+
+ if (0 != tcsetattr(STDIN_FILENO, TCSANOW, &tty))
throw std::runtime_error(std::string("setTerminalEcho failed set: ") + errnoToString(errno));
-#endif
}
diff --git a/base/common/strong_typedef.h b/base/common/strong_typedef.h
index d9850a25c37..77b83bfa6e5 100644
--- a/base/common/strong_typedef.h
+++ b/base/common/strong_typedef.h
@@ -12,6 +12,7 @@ private:
T t;
public:
+ using UnderlyingType = T;
template ::type>
explicit StrongTypedef(const T & t_) : t(t_) {}
template ::type>
diff --git a/base/common/tests/CMakeLists.txt b/base/common/tests/CMakeLists.txt
index 6775d443fb6..2a07a94055f 100644
--- a/base/common/tests/CMakeLists.txt
+++ b/base/common/tests/CMakeLists.txt
@@ -1,25 +1,2 @@
-include (${ClickHouse_SOURCE_DIR}/cmake/add_check.cmake)
-
-add_executable (date_lut2 date_lut2.cpp)
-add_executable (date_lut3 date_lut3.cpp)
-add_executable (date_lut_default_timezone date_lut_default_timezone.cpp)
-add_executable (local_date_time_comparison local_date_time_comparison.cpp)
-add_executable (realloc-perf allocator.cpp)
-
-set(PLATFORM_LIBS ${CMAKE_DL_LIBS})
-
-target_link_libraries (date_lut2 PRIVATE common ${PLATFORM_LIBS})
-target_link_libraries (date_lut3 PRIVATE common ${PLATFORM_LIBS})
-target_link_libraries (date_lut_default_timezone PRIVATE common ${PLATFORM_LIBS})
-target_link_libraries (local_date_time_comparison PRIVATE common ${PLATFORM_LIBS})
-target_link_libraries (realloc-perf PRIVATE common)
-add_check(local_date_time_comparison)
-
-if(USE_GTEST)
- add_executable(unit_tests_libcommon gtest_json_test.cpp gtest_strong_typedef.cpp gtest_find_symbols.cpp)
- target_link_libraries(unit_tests_libcommon PRIVATE common ${GTEST_MAIN_LIBRARIES} ${GTEST_LIBRARIES})
- add_check(unit_tests_libcommon)
-endif()
-
add_executable (dump_variable dump_variable.cpp)
target_link_libraries (dump_variable PRIVATE clickhouse_common_io)
diff --git a/base/common/tests/allocator.cpp b/base/common/tests/allocator.cpp
deleted file mode 100644
index 03f6228e0f5..00000000000
--- a/base/common/tests/allocator.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-#include
-#include
-#include
-#include
-
-
-void thread_func()
-{
- for (size_t i = 0; i < 100; ++i)
- {
- size_t size = 4096;
-
- void * buf = malloc(size);
- if (!buf)
- abort();
- memset(buf, 0, size);
-
- while (size < 1048576)
- {
- size_t next_size = size * 4;
-
- void * new_buf = realloc(buf, next_size);
- if (!new_buf)
- abort();
- buf = new_buf;
-
- memset(reinterpret_cast(buf) + size, 0, next_size - size);
- size = next_size;
- }
-
- free(buf);
- }
-}
-
-
-int main(int, char **)
-{
- std::vector threads(16);
- for (size_t i = 0; i < 1000; ++i)
- {
- for (auto & thread : threads)
- thread = std::thread(thread_func);
- for (auto & thread : threads)
- thread.join();
- }
- return 0;
-}
diff --git a/base/common/tests/date_lut2.cpp b/base/common/tests/date_lut2.cpp
deleted file mode 100644
index 6dcf5e8adf2..00000000000
--- a/base/common/tests/date_lut2.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-#include
-#include
-
-#include
-
-
-static std::string toString(time_t Value)
-{
- struct tm tm;
- char buf[96];
-
- localtime_r(&Value, &tm);
- snprintf(buf, sizeof(buf), "%04d-%02d-%02d %02d:%02d:%02d",
- tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
-
- return buf;
-}
-
-static time_t orderedIdentifierToDate(unsigned value)
-{
- struct tm tm;
-
- memset(&tm, 0, sizeof(tm));
-
- tm.tm_year = value / 10000 - 1900;
- tm.tm_mon = (value % 10000) / 100 - 1;
- tm.tm_mday = value % 100;
- tm.tm_isdst = -1;
-
- return mktime(&tm);
-}
-
-
-void loop(time_t begin, time_t end, int step)
-{
- const auto & date_lut = DateLUT::instance();
-
- for (time_t t = begin; t < end; t += step)
- std::cout << toString(t)
- << ", " << toString(date_lut.toTime(t))
- << ", " << date_lut.toHour(t)
- << std::endl;
-}
-
-
-int main(int, char **)
-{
- loop(orderedIdentifierToDate(20101031), orderedIdentifierToDate(20101101), 15 * 60);
- loop(orderedIdentifierToDate(20100328), orderedIdentifierToDate(20100330), 15 * 60);
- loop(orderedIdentifierToDate(20141020), orderedIdentifierToDate(20141106), 15 * 60);
-
- return 0;
-}
diff --git a/base/common/tests/date_lut3.cpp b/base/common/tests/date_lut3.cpp
deleted file mode 100644
index 411765d2b2a..00000000000
--- a/base/common/tests/date_lut3.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-#include
-#include
-
-#include
-
-#include
-
-
-static std::string toString(time_t Value)
-{
- struct tm tm;
- char buf[96];
-
- localtime_r(&Value, &tm);
- snprintf(buf, sizeof(buf), "%04d-%02d-%02d %02d:%02d:%02d",
- tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
-
- return buf;
-}
-
-static time_t orderedIdentifierToDate(unsigned value)
-{
- struct tm tm;
-
- memset(&tm, 0, sizeof(tm));
-
- tm.tm_year = value / 10000 - 1900;
- tm.tm_mon = (value % 10000) / 100 - 1;
- tm.tm_mday = value % 100;
- tm.tm_isdst = -1;
-
- return mktime(&tm);
-}
-
-
-void loop(time_t begin, time_t end, int step)
-{
- const auto & date_lut = DateLUT::instance();
-
- for (time_t t = begin; t < end; t += step)
- {
- time_t t2 = date_lut.makeDateTime(date_lut.toYear(t), date_lut.toMonth(t), date_lut.toDayOfMonth(t),
- date_lut.toHour(t), date_lut.toMinute(t), date_lut.toSecond(t));
-
- std::string s1 = toString(t);
- std::string s2 = toString(t2);
-
- std::cerr << s1 << ", " << s2 << std::endl;
-
- if (s1 != s2)
- throw Poco::Exception("Test failed.");
- }
-}
-
-
-int main(int, char **)
-{
- loop(orderedIdentifierToDate(20101031), orderedIdentifierToDate(20101101), 15 * 60);
- loop(orderedIdentifierToDate(20100328), orderedIdentifierToDate(20100330), 15 * 60);
-
- return 0;
-}
diff --git a/base/common/tests/date_lut_default_timezone.cpp b/base/common/tests/date_lut_default_timezone.cpp
deleted file mode 100644
index b8e5aa08931..00000000000
--- a/base/common/tests/date_lut_default_timezone.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-#include
-#include
-#include
-
-int main(int, char **)
-{
- try
- {
- const auto & date_lut = DateLUT::instance();
- std::cout << "Detected default timezone: `" << date_lut.getTimeZone() << "'" << std::endl;
- time_t now = time(nullptr);
- std::cout << "Current time: " << date_lut.timeToString(now)
- << ", UTC: " << DateLUT::instance("UTC").timeToString(now) << std::endl;
- }
- catch (const Poco::Exception & e)
- {
- std::cerr << e.displayText() << std::endl;
- return 1;
- }
- catch (std::exception & e)
- {
- std::cerr << "std::exception: " << e.what() << std::endl;
- return 2;
- }
- catch (...)
- {
- std::cerr << "Some exception" << std::endl;
- return 3;
- }
- return 0;
-}
diff --git a/base/common/tests/gtest_json_test.cpp b/base/common/tests/gtest_json_test.cpp
deleted file mode 100644
index 189a1a03d99..00000000000
--- a/base/common/tests/gtest_json_test.cpp
+++ /dev/null
@@ -1,656 +0,0 @@
-#include
-#include
-#include
-#include
-
-#include
-
-using namespace std::literals::string_literals;
-
-#include
-
-enum class ResultType
-{
- Return,
- Throw
-};
-
-struct GetStringTestRecord
-{
- const char * input;
- ResultType result_type;
- const char * result;
-};
-
-TEST(JSONSuite, SimpleTest)
-{
- std::vector test_data =
- {
- { R"("name")", ResultType::Return, "name" },
- { R"("Вафельница Vitek WX-1102 FL")", ResultType::Return, "Вафельница Vitek WX-1102 FL" },
- { R"("brand")", ResultType::Return, "brand" },
- { R"("184509")", ResultType::Return, "184509" },
- { R"("category")", ResultType::Return, "category" },
- { R"("Все для детей/Детская техника/Vitek")", ResultType::Return, "Все для детей/Детская техника/Vitek" },
- { R"("variant")", ResultType::Return, "variant" },
- { R"("В наличии")", ResultType::Return, "В наличии" },
- { R"("price")", ResultType::Return, "price" },
- { R"("2390.00")", ResultType::Return, "2390.00" },
- { R"("list")", ResultType::Return, "list" },
- { R"("Карточка")", ResultType::Return, "Карточка" },
- { R"("position")", ResultType::Return, "position" },
- { R"("detail")", ResultType::Return, "detail" },
- { R"("actionField")", ResultType::Return, "actionField" },
- { R"("list")", ResultType::Return, "list" },
- { R"("http://www.techport.ru/q/?t=вафельница&sort=price&sdim=asc")", ResultType::Return, "http://www.techport.ru/q/?t=вафельница&sort=price&sdim=asc" },
- { R"("action")", ResultType::Return, "action" },
- { R"("detail")", ResultType::Return, "detail" },
- { R"("products")", ResultType::Return, "products" },
- { R"("name")", ResultType::Return, "name" },
- { R"("Вафельница Vitek WX-1102 FL")", ResultType::Return, "Вафельница Vitek WX-1102 FL" },
- { R"("id")", ResultType::Return, "id" },
- { R"("184509")", ResultType::Return, "184509" },
- { R"("price")", ResultType::Return, "price" },
- { R"("2390.00")", ResultType::Return, "2390.00" },
- { R"("brand")", ResultType::Return, "brand" },
- { R"("Vitek")", ResultType::Return, "Vitek" },
- { R"("category")", ResultType::Return, "category" },
- { R"("Все для детей/Детская техника/Vitek")", ResultType::Return, "Все для детей/Детская техника/Vitek" },
- { R"("variant")", ResultType::Return, "variant" },
- { R"("В наличии")", ResultType::Return, "В наличии" },
- { R"("ru")", ResultType::Return, "ru" },
- { R"("experiments")", ResultType::Return, "experiments" },
- { R"("lang")", ResultType::Return, "lang" },
- { R"("ru")", ResultType::Return, "ru" },
- { R"("los_portal")", ResultType::Return, "los_portal" },
- { R"("los_level")", ResultType::Return, "los_level" },
- { R"("none")", ResultType::Return, "none" },
- { R"("isAuthorized")", ResultType::Return, "isAuthorized" },
- { R"("isSubscriber")", ResultType::Return, "isSubscriber" },
- { R"("postType")", ResultType::Return, "postType" },
- { R"("Новости")", ResultType::Return, "Новости" },
- { R"("experiments")", ResultType::Return, "experiments" },
- { R"("lang")", ResultType::Return, "lang" },
- { R"("ru")", ResultType::Return, "ru" },
- { R"("los_portal")", ResultType::Return, "los_portal" },
- { R"("los_level")", ResultType::Return, "los_level" },
- { R"("none")", ResultType::Return, "none" },
- { R"("lang")", ResultType::Return, "lang" },
- { R"("ru")", ResultType::Return, "ru" },
- { R"("Электроплита GEFEST Брест ЭПНД 5140-01 0001")", ResultType::Return, "Электроплита GEFEST Брест ЭПНД 5140-01 0001" },
- { R"("price")", ResultType::Return, "price" },
- { R"("currencyCode")", ResultType::Return, "currencyCode" },
- { R"("RUB")", ResultType::Return, "RUB" },
- { R"("lang")", ResultType::Return, "lang" },
- { R"("ru")", ResultType::Return, "ru" },
- { R"("experiments")", ResultType::Return, "experiments" },
- { R"("lang")", ResultType::Return, "lang" },
- { R"("ru")", ResultType::Return, "ru" },
- { R"("los_portal")", ResultType::Return, "los_portal" },
- { R"("los_level")", ResultType::Return, "los_level" },
- { R"("none")", ResultType::Return, "none" },
- { R"("trash_login")", ResultType::Return, "trash_login" },
- { R"("novikoff")", ResultType::Return, "novikoff" },
- { R"("trash_cat_link")", ResultType::Return, "trash_cat_link" },
- { R"("progs")", ResultType::Return, "progs" },
- { R"("trash_parent_link")", ResultType::Return, "trash_parent_link" },
- { R"("content")", ResultType::Return, "content" },
- { R"("trash_posted_parent")", ResultType::Return, "trash_posted_parent" },
- { R"("content.01.2016")", ResultType::Return, "content.01.2016" },
- { R"("trash_posted_cat")", ResultType::Return, "trash_posted_cat" },
- { R"("progs.01.2016")", ResultType::Return, "progs.01.2016" },
- { R"("trash_virus_count")", ResultType::Return, "trash_virus_count" },
- { R"("trash_is_android")", ResultType::Return, "trash_is_android" },
- { R"("trash_is_wp8")", ResultType::Return, "trash_is_wp8" },
- { R"("trash_is_ios")", ResultType::Return, "trash_is_ios" },
- { R"("trash_posted")", ResultType::Return, "trash_posted" },
- { R"("01.2016")", ResultType::Return, "01.2016" },
- { R"("experiments")", ResultType::Return, "experiments" },
- { R"("lang")", ResultType::Return, "lang" },
- { R"("ru")", ResultType::Return, "ru" },
- { R"("los_portal")", ResultType::Return, "los_portal" },
- { R"("los_level")", ResultType::Return, "los_level" },
- { R"("none")", ResultType::Return, "none" },
- { R"("merchantId")", ResultType::Return, "merchantId" },
- { R"("13694_49246")", ResultType::Return, "13694_49246" },
- { R"("cps-source")", ResultType::Return, "cps-source" },
- { R"("wargaming")", ResultType::Return, "wargaming" },
- { R"("cps_provider")", ResultType::Return, "cps_provider" },
- { R"("default")", ResultType::Return, "default" },
- { R"("errorReason")", ResultType::Return, "errorReason" },
- { R"("no errors")", ResultType::Return, "no errors" },
- { R"("scid")", ResultType::Return, "scid" },
- { R"("isAuthPayment")", ResultType::Return, "isAuthPayment" },
- { R"("lang")", ResultType::Return, "lang" },
- { R"("ru")", ResultType::Return, "ru" },
- { R"("rubric")", ResultType::Return, "rubric" },
- { R"("")", ResultType::Return, "" },
- { R"("rubric")", ResultType::Return, "rubric" },
- { R"("Мир")", ResultType::Return, "Мир" },
- { R"("lang")", ResultType::Return, "lang" },
- { R"("ru")", ResultType::Return, "ru" },
- { R"("experiments")", ResultType::Return, "experiments" },
- { R"("lang")", ResultType::Return, "lang" },
- { R"("ru")", ResultType::Return, "ru" },
- { R"("los_portal")", ResultType::Return, "los_portal" },
- { R"("los_level")", ResultType::Return, "los_level" },
- { R"("none")", ResultType::Return, "none" },
- { R"("lang")", ResultType::Return, "lang" },
- { R"("ru")", ResultType::Return, "ru" },
- { R"("__ym")", ResultType::Return, "__ym" },
- { R"("ecommerce")", ResultType::Return, "ecommerce" },
- { R"("impressions")", ResultType::Return, "impressions" },
- { R"("id")", ResultType::Return, "id" },
- { R"("863813")", ResultType::Return, "863813" },
- { R"("name")", ResultType::Return, "name" },
- { R"("Футболка детская 3D Happy, возраст 1-2 года, трикотаж")", ResultType::Return, "Футболка детская 3D Happy, возраст 1-2 года, трикотаж" },
- { R"("category")", ResultType::Return, "category" },
- { R"("/Летние товары/Летний текстиль/")", ResultType::Return, "/Летние товары/Летний текстиль/" },
- { R"("variant")", ResultType::Return, "variant" },
- { R"("")", ResultType::Return, "" },
- { R"("price")", ResultType::Return, "price" },
- { R"("390.00")", ResultType::Return, "390.00" },
- { R"("list")", ResultType::Return, "list" },
- { R"("/retailrocket/")", ResultType::Return, "/retailrocket/" },
- { R"("position")", ResultType::Return, "position" },
- { R"("brand")", ResultType::Return, "brand" },
- { R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")", ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/" },
- { R"("id")", ResultType::Return, "id" },
- { R"("863839")", ResultType::Return, "863839" },
- { R"("name")", ResultType::Return, "name" },
- { R"("Футболка детская 3D Pretty kitten, возраст 1-2 года, трикотаж")", ResultType::Return, "Футболка детская 3D Pretty kitten, возраст 1-2 года, трикотаж" },
- { R"("category")", ResultType::Return, "category" },
- { R"("/Летние товары/Летний текстиль/")", ResultType::Return, "/Летние товары/Летний текстиль/" },
- { R"("variant")", ResultType::Return, "variant" },
- { R"("")", ResultType::Return, "" },
- { R"("price")", ResultType::Return, "price" },
- { R"("390.00")", ResultType::Return, "390.00" },
- { R"("list")", ResultType::Return, "list" },
- { R"("/retailrocket/")", ResultType::Return, "/retailrocket/" },
- { R"("position")", ResultType::Return, "position" },
- { R"("brand")", ResultType::Return, "brand" },
- { R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")", ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/" },
- { R"("id")", ResultType::Return, "id" },
- { R"("863847")", ResultType::Return, "863847" },
- { R"("name")", ResultType::Return, "name" },
- { R"("Футболка детская 3D Little tiger, возраст 1-2 года, трикотаж")", ResultType::Return, "Футболка детская 3D Little tiger, возраст 1-2 года, трикотаж" },
- { R"("category")", ResultType::Return, "category" },
- { R"("/Летние товары/Летний текстиль/")", ResultType::Return, "/Летние товары/Летний текстиль/" },
- { R"("variant")", ResultType::Return, "variant" },
- { R"("")", ResultType::Return, "" },
- { R"("price")", ResultType::Return, "price" },
- { R"("390.00")", ResultType::Return, "390.00" },
- { R"("list")", ResultType::Return, "list" },
- { R"("/retailrocket/")", ResultType::Return, "/retailrocket/" },
- { R"("position")", ResultType::Return, "position" },
- { R"("brand")", ResultType::Return, "brand" },
- { R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")", ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/" },
- { R"("id")", ResultType::Return, "id" },
- { R"("911480")", ResultType::Return, "911480" },
- { R"("name")", ResultType::Return, "name" },
- { R"("Футболка детская 3D Puppy, возраст 1-2 года, трикотаж")", ResultType::Return, "Футболка детская 3D Puppy, возраст 1-2 года, трикотаж" },
- { R"("category")", ResultType::Return, "category" },
- { R"("/Летние товары/Летний текстиль/")", ResultType::Return, "/Летние товары/Летний текстиль/" },
- { R"("variant")", ResultType::Return, "variant" },
- { R"("")", ResultType::Return, "" },
- { R"("price")", ResultType::Return, "price" },
- { R"("390.00")", ResultType::Return, "390.00" },
- { R"("list")", ResultType::Return, "list" },
- { R"("/retailrocket/")", ResultType::Return, "/retailrocket/" },
- { R"("position")", ResultType::Return, "position" },
- { R"("brand")", ResultType::Return, "brand" },
- { R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")", ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/" },
- { R"("id")", ResultType::Return, "id" },
- { R"("911484")", ResultType::Return, "911484" },
- { R"("name")", ResultType::Return, "name" },
- { R"("Футболка детская 3D Little bears, возраст 1-2 года, трикотаж")", ResultType::Return, "Футболка детская 3D Little bears, возраст 1-2 года, трикотаж" },
- { R"("category")", ResultType::Return, "category" },
- { R"("/Летние товары/Летний текстиль/")", ResultType::Return, "/Летние товары/Летний текстиль/" },
- { R"("variant")", ResultType::Return, "variant" },
- { R"("")", ResultType::Return, "" },
- { R"("price")", ResultType::Return, "price" },
- { R"("390.00")", ResultType::Return, "390.00" },
- { R"("list")", ResultType::Return, "list" },
- { R"("/retailrocket/")", ResultType::Return, "/retailrocket/" },
- { R"("position")", ResultType::Return, "position" },
- { R"("brand")", ResultType::Return, "brand" },
- { R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")", ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/" },
- { R"("id")", ResultType::Return, "id" },
- { R"("911489")", ResultType::Return, "911489" },
- { R"("name")", ResultType::Return, "name" },
- { R"("Футболка детская 3D Dolphin, возраст 2-4 года, трикотаж")", ResultType::Return, "Футболка детская 3D Dolphin, возраст 2-4 года, трикотаж" },
- { R"("category")", ResultType::Return, "category" },
- { R"("/Летние товары/Летний текстиль/")", ResultType::Return, "/Летние товары/Летний текстиль/" },
- { R"("variant")", ResultType::Return, "variant" },
- { R"("")", ResultType::Return, "" },
- { R"("price")", ResultType::Return, "price" },
- { R"("390.00")", ResultType::Return, "390.00" },
- { R"("list")", ResultType::Return, "list" },
- { R"("/retailrocket/")", ResultType::Return, "/retailrocket/" },
- { R"("position")", ResultType::Return, "position" },
- { R"("brand")", ResultType::Return, "brand" },
- { R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")", ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/" },
- { R"("id")", ResultType::Return, "id" },
- { R"("911496")", ResultType::Return, "911496" },
- { R"("name")", ResultType::Return, "name" },
- { R"("Футболка детская 3D Pretty, возраст 1-2 года, трикотаж")", ResultType::Return, "Футболка детская 3D Pretty, возраст 1-2 года, трикотаж" },
- { R"("category")", ResultType::Return, "category" },
- { R"("/Летние товары/Летний текстиль/")", ResultType::Return, "/Летние товары/Летний текстиль/" },
- { R"("variant")", ResultType::Return, "variant" },
- { R"("")", ResultType::Return, "" },
- { R"("price")", ResultType::Return, "price" },
- { R"("390.00")", ResultType::Return, "390.00" },
- { R"("list")", ResultType::Return, "list" },
- { R"("/retailrocket/")", ResultType::Return, "/retailrocket/" },
- { R"("position")", ResultType::Return, "position" },
- { R"("brand")", ResultType::Return, "brand" },
- { R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")", ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/" },
- { R"("id")", ResultType::Return, "id" },
- { R"("911504")", ResultType::Return, "911504" },
- { R"("name")", ResultType::Return, "name" },
- { R"("Футболка детская 3D Fairytale, возраст 1-2 года, трикотаж")", ResultType::Return, "Футболка детская 3D Fairytale, возраст 1-2 года, трикотаж" },
- { R"("category")", ResultType::Return, "category" },
- { R"("/Летние товары/Летний текстиль/")", ResultType::Return, "/Летние товары/Летний текстиль/" },
- { R"("variant")", ResultType::Return, "variant" },
- { R"("")", ResultType::Return, "" },
- { R"("price")", ResultType::Return, "price" },
- { R"("390.00")", ResultType::Return, "390.00" },
- { R"("list")", ResultType::Return, "list" },
- { R"("/retailrocket/")", ResultType::Return, "/retailrocket/" },
- { R"("position")", ResultType::Return, "position" },
- { R"("brand")", ResultType::Return, "brand" },
- { R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")", ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/" },
- { R"("id")", ResultType::Return, "id" },
- { R"("911508")", ResultType::Return, "911508" },
- { R"("name")", ResultType::Return, "name" },
- { R"("Футболка детская 3D Kittens, возраст 1-2 года, трикотаж")", ResultType::Return, "Футболка детская 3D Kittens, возраст 1-2 года, трикотаж" },
- { R"("category")", ResultType::Return, "category" },
- { R"("/Летние товары/Летний текстиль/")", ResultType::Return, "/Летние товары/Летний текстиль/" },
- { R"("variant")", ResultType::Return, "variant" },
- { R"("")", ResultType::Return, "" },
- { R"("price")", ResultType::Return, "price" },
- { R"("390.00")", ResultType::Return, "390.00" },
- { R"("list")", ResultType::Return, "list" },
- { R"("/retailrocket/")", ResultType::Return, "/retailrocket/" },
- { R"("position")", ResultType::Return, "position" },
- { R"("brand")", ResultType::Return, "brand" },
- { R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")", ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/" },
- { R"("id")", ResultType::Return, "id" },
- { R"("911512")", ResultType::Return, "911512" },
- { R"("name")", ResultType::Return, "name" },
- { R"("Футболка детская 3D Sunshine, возраст 1-2 года, трикотаж")", ResultType::Return, "Футболка детская 3D Sunshine, возраст 1-2 года, трикотаж" },
- { R"("category")", ResultType::Return, "category" },
- { R"("/Летние товары/Летний текстиль/")", ResultType::Return, "/Летние товары/Летний текстиль/" },
- { R"("variant")", ResultType::Return, "variant" },
- { R"("")", ResultType::Return, "" },
- { R"("price")", ResultType::Return, "price" },
- { R"("390.00")", ResultType::Return, "390.00" },
- { R"("list")", ResultType::Return, "list" },
- { R"("/retailrocket/")", ResultType::Return, "/retailrocket/" },
- { R"("position")", ResultType::Return, "position" },
- { R"("brand")", ResultType::Return, "brand" },
- { R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")", ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/" },
- { R"("id")", ResultType::Return, "id" },
- { R"("911516")", ResultType::Return, "911516" },
- { R"("name")", ResultType::Return, "name" },
- { R"("Футболка детская 3D Dog in bag, возраст 1-2 года, трикотаж")", ResultType::Return, "Футболка детская 3D Dog in bag, возраст 1-2 года, трикотаж" },
- { R"("category")", ResultType::Return, "category" },
- { R"("/Летние товары/Летний текстиль/")", ResultType::Return, "/Летние товары/Летний текстиль/" },
- { R"("variant")", ResultType::Return, "variant" },
- { R"("")", ResultType::Return, "" },
- { R"("price")", ResultType::Return, "price" },
- { R"("390.00")", ResultType::Return, "390.00" },
- { R"("list")", ResultType::Return, "list" },
- { R"("/retailrocket/")", ResultType::Return, "/retailrocket/" },
- { R"("position")", ResultType::Return, "position" },
- { R"("brand")", ResultType::Return, "brand" },
- { R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")", ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/" },
- { R"("id")", ResultType::Return, "id" },
- { R"("911520")", ResultType::Return, "911520" },
- { R"("name")", ResultType::Return, "name" },
- { R"("Футболка детская 3D Cute puppy, возраст 1-2 года, трикотаж")", ResultType::Return, "Футболка детская 3D Cute puppy, возраст 1-2 года, трикотаж" },
- { R"("category")", ResultType::Return, "category" },
- { R"("/Летние товары/Летний текстиль/")", ResultType::Return, "/Летние товары/Летний текстиль/" },
- { R"("variant")", ResultType::Return, "variant" },
- { R"("")", ResultType::Return, "" },
- { R"("price")", ResultType::Return, "price" },
- { R"("390.00")", ResultType::Return, "390.00" },
- { R"("list")", ResultType::Return, "list" },
- { R"("/retailrocket/")", ResultType::Return, "/retailrocket/" },
- { R"("position")", ResultType::Return, "position" },
- { R"("brand")", ResultType::Return, "brand" },
- { R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")", ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/" },
- { R"("id")", ResultType::Return, "id" },
- { R"("911524")", ResultType::Return, "911524" },
- { R"("name")", ResultType::Return, "name" },
- { R"("Футболка детская 3D Rabbit, возраст 1-2 года, трикотаж")", ResultType::Return, "Футболка детская 3D Rabbit, возраст 1-2 года, трикотаж" },
- { R"("category")", ResultType::Return, "category" },
- { R"("/Летние товары/Летний текстиль/")", ResultType::Return, "/Летние товары/Летний текстиль/" },
- { R"("variant")", ResultType::Return, "variant" },
- { R"("")", ResultType::Return, "" },
- { R"("price")", ResultType::Return, "price" },
- { R"("390.00")", ResultType::Return, "390.00" },
- { R"("list")", ResultType::Return, "list" },
- { R"("/retailrocket/")", ResultType::Return, "/retailrocket/" },
- { R"("position")", ResultType::Return, "position" },
- { R"("brand")", ResultType::Return, "brand" },
- { R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")", ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/" },
- { R"("id")", ResultType::Return, "id" },
- { R"("911528")", ResultType::Return, "911528" },
- { R"("name")", ResultType::Return, "name" },
- { R"("Футболка детская 3D Turtle, возраст 1-2 года, трикотаж")", ResultType::Return, "Футболка детская 3D Turtle, возраст 1-2 года, трикотаж" },
- { R"("category")", ResultType::Return, "category" },
- { R"("/Летние товары/Летний текстиль/")", ResultType::Return, "/Летние товары/Летний текстиль/" },
- { R"("variant")", ResultType::Return, "variant" },
- { R"("")", ResultType::Return, "" },
- { R"("price")", ResultType::Return, "price" },
- { R"("390.00")", ResultType::Return, "390.00" },
- { R"("list")", ResultType::Return, "list" },
- { R"("/retailrocket/")", ResultType::Return, "/retailrocket/" },
- { R"("position")", ResultType::Return, "position" },
- { R"("brand")", ResultType::Return, "brand" },
- { R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")", ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/" },
- { R"("id")", ResultType::Return, "id" },
- { R"("888616")", ResultType::Return, "888616" },
- { R"("name")", ResultType::Return, "name" },
- { "\"3Д Футболка мужская \\\"Collorista\\\" Светлое завтра р-р XL(52-54), 100% хлопок, трикотаж\"", ResultType::Return, "3Д Футболка мужская \"Collorista\" Светлое завтра р-р XL(52-54), 100% хлопок, трикотаж" },
- { R"("category")", ResultType::Return, "category" },
- { R"("/Одежда и обувь/Мужская одежда/Футболки/")", ResultType::Return, "/Одежда и обувь/Мужская одежда/Футболки/" },
- { R"("variant")", ResultType::Return, "variant" },
- { R"("")", ResultType::Return, "" },
- { R"("price")", ResultType::Return, "price" },
- { R"("406.60")", ResultType::Return, "406.60" },
- { R"("list")", ResultType::Return, "list" },
- { R"("/retailrocket/")", ResultType::Return, "/retailrocket/" },
- { R"("position")", ResultType::Return, "position" },
- { R"("brand")", ResultType::Return, "brand" },
- { R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")", ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/" },
- { R"("id")", ResultType::Return, "id" },
- { R"("913361")", ResultType::Return, "913361" },
- { R"("name")", ResultType::Return, "name" },
- { R"("3Д Футболка детская World р-р 8-10, 100% хлопок, трикотаж")", ResultType::Return, "3Д Футболка детская World р-р 8-10, 100% хлопок, трикотаж" },
- { R"("category")", ResultType::Return, "category" },
- { R"("/Летние товары/Летний текстиль/")", ResultType::Return, "/Летние товары/Летний текстиль/" },
- { R"("variant")", ResultType::Return, "variant" },
- { R"("")", ResultType::Return, "" },
- { R"("price")", ResultType::Return, "price" },
- { R"("470.00")", ResultType::Return, "470.00" },
- { R"("list")", ResultType::Return, "list" },
- { R"("/retailrocket/")", ResultType::Return, "/retailrocket/" },
- { R"("position")", ResultType::Return, "position" },
- { R"("brand")", ResultType::Return, "brand" },
- { R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")", ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/" },
- { R"("id")", ResultType::Return, "id" },
- { R"("913364")", ResultType::Return, "913364" },
- { R"("name")", ResultType::Return, "name" },
- { R"("3Д Футболка детская Force р-р 8-10, 100% хлопок, трикотаж")", ResultType::Return, "3Д Футболка детская Force р-р 8-10, 100% хлопок, трикотаж" },
- { R"("category")", ResultType::Return, "category" },
- { R"("/Летние товары/Летний текстиль/")", ResultType::Return, "/Летние товары/Летний текстиль/" },
- { R"("variant")", ResultType::Return, "variant" },
- { R"("")", ResultType::Return, "" },
- { R"("price")", ResultType::Return, "price" },
- { R"("470.00")", ResultType::Return, "470.00" },
- { R"("list")", ResultType::Return, "list" },
- { R"("/retailrocket/")", ResultType::Return, "/retailrocket/" },
- { R"("position")", ResultType::Return, "position" },
- { R"("brand")", ResultType::Return, "brand" },
- { R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")", ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/" },
- { R"("id")", ResultType::Return, "id" },
- { R"("913367")", ResultType::Return, "913367" },
- { R"("name")", ResultType::Return, "name" },
- { R"("3Д Футболка детская Winter tale р-р 8-10, 100% хлопок, трикотаж")", ResultType::Return, "3Д Футболка детская Winter tale р-р 8-10, 100% хлопок, трикотаж" },
- { R"("category")", ResultType::Return, "category" },
- { R"("/Летние товары/Летний текстиль/")", ResultType::Return, "/Летние товары/Летний текстиль/" },
- { R"("variant")", ResultType::Return, "variant" },
- { R"("")", ResultType::Return, "" },
- { R"("price")", ResultType::Return, "price" },
- { R"("470.00")", ResultType::Return, "470.00" },
- { R"("list")", ResultType::Return, "list" },
- { R"("/retailrocket/")", ResultType::Return, "/retailrocket/" },
- { R"("position")", ResultType::Return, "position" },
- { R"("brand")", ResultType::Return, "brand" },
- { R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")", ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/" },
- { R"("id")", ResultType::Return, "id" },
- { R"("913385")", ResultType::Return, "913385" },
- { R"("name")", ResultType::Return, "name" },
- { R"("3Д Футболка детская Moonshine р-р 8-10, 100% хлопок, трикотаж")", ResultType::Return, "3Д Футболка детская Moonshine р-р 8-10, 100% хлопок, трикотаж" },
- { R"("category")", ResultType::Return, "category" },
- { R"("/Летние товары/Летний текстиль/")", ResultType::Return, "/Летние товары/Летний текстиль/" },
- { R"("variant")", ResultType::Return, "variant" },
- { R"("")", ResultType::Return, "" },
- { R"("price")", ResultType::Return, "price" },
- { R"("470.00")", ResultType::Return, "470.00" },
- { R"("list")", ResultType::Return, "list" },
- { R"("/retailrocket/")", ResultType::Return, "/retailrocket/" },
- { R"("position")", ResultType::Return, "position" },
- { R"("brand")", ResultType::Return, "brand" },
- { R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")", ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/" },
- { R"("id")", ResultType::Return, "id" },
- { R"("913391")", ResultType::Return, "913391" },
- { R"("name")", ResultType::Return, "name" },
- { R"("3Д Футболка детская Shaman р-р 8-10, 100% хлопок, трикотаж")", ResultType::Return, "3Д Футболка детская Shaman р-р 8-10, 100% хлопок, трикотаж" },
- { R"("category")", ResultType::Return, "category" },
- { R"("/Летние товары/Летний текстиль/")", ResultType::Return, "/Летние товары/Летний текстиль/" },
- { R"("variant")", ResultType::Return, "variant" },
- { R"("")", ResultType::Return, "" },
- { R"("price")", ResultType::Return, "price" },
- { R"("470.00")", ResultType::Return, "470.00" },
- { R"("list")", ResultType::Return, "list" },
- { R"("/retailrocket/")", ResultType::Return, "/retailrocket/" },
- { R"("position")", ResultType::Return, "position" },
- { R"("brand")", ResultType::Return, "brand" },
- { R"("/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/")", ResultType::Return, "/911488/futbolka-detskaya-3d-dolphin-vozrast-1-2-goda-trikotazh/" },
- { R"("usertype")", ResultType::Return, "usertype" },
- { R"("visitor")", ResultType::Return, "visitor" },
- { R"("lang")", ResultType::Return, "lang" },
- { R"("ru")", ResultType::Return, "ru" },
- { R"("__ym")", ResultType::Return, "__ym" },
- { R"("ecommerce")", ResultType::Return, "ecommerce" },
- { R"("impressions")", ResultType::Return, "impressions" },
- { R"("experiments")", ResultType::Return, "experiments" },
- { R"("lang")", ResultType::Return, "lang" },
- { R"("ru")", ResultType::Return, "ru" },
- { R"("los_portal")", ResultType::Return, "los_portal" },
- { R"("los_level")", ResultType::Return, "los_level" },
- { R"("none")", ResultType::Return, "none" },
- { R"("experiments")", ResultType::Return, "experiments" },
- { R"("lang")", ResultType::Return, "lang" },
- { R"("ru")", ResultType::Return, "ru" },
- { R"("los_portal")", ResultType::Return, "los_portal" },
- { R"("los_level")", ResultType::Return, "los_level" },
- { R"("none")", ResultType::Return, "none" },
- { R"("experiments")", ResultType::Return, "experiments" },
- { R"("lang")", ResultType::Return, "lang" },
- { R"("ru")", ResultType::Return, "ru" },
- { R"("los_portal")", ResultType::Return, "los_portal" },
- { R"("los_level")", ResultType::Return, "los_level" },
- { R"("none")", ResultType::Return, "none" },
- { R"("experiments")", ResultType::Return, "experiments" },
- { R"("lang")", ResultType::Return, "lang" },
- { R"("ru")", ResultType::Return, "ru" },
- { R"("los_portal")", ResultType::Return, "los_portal" },
- { R"("los_level")", ResultType::Return, "los_level" },
- { R"("none")", ResultType::Return, "none" },
- { R"("experiments")", ResultType::Return, "experiments" },
- { R"("lang")", ResultType::Return, "lang" },
- { R"("ru")", ResultType::Return, "ru" },
- { R"("los_portal")", ResultType::Return, "los_portal" },
- { R"("los_level")", ResultType::Return, "los_level" },
- { R"("none")", ResultType::Return, "none" },
- { R"("__ym")", ResultType::Return, "__ym" },
- { R"("ecommerce")", ResultType::Return, "ecommerce" },
- { R"("currencyCode")", ResultType::Return, "currencyCode" },
- { R"("RUR")", ResultType::Return, "RUR" },
- { R"("impressions")", ResultType::Return, "impressions" },
- { R"("name")", ResultType::Return, "name" },
- { R"("Чайник электрический Mystery MEK-1627, белый")", ResultType::Return, "Чайник электрический Mystery MEK-1627, белый" },
- { R"("brand")", ResultType::Return, "brand" },
- { R"("Mystery")", ResultType::Return, "Mystery" },
- { R"("id")", ResultType::Return, "id" },
- { R"("187180")", ResultType::Return, "187180" },
- { R"("category")", ResultType::Return, "category" },
- { R"("Мелкая бытовая техника/Мелкие кухонные приборы/Чайники электрические/Mystery")", ResultType::Return, "Мелкая бытовая техника/Мелкие кухонные приборы/Чайники электрические/Mystery" },
- { R"("variant")", ResultType::Return, "variant" },
- { R"("В наличии")", ResultType::Return, "В наличии" },
- { R"("price")", ResultType::Return, "price" },
- { R"("1630.00")", ResultType::Return, "1630.00" },
- { R"("list")", ResultType::Return, "list" },
- { R"("Карточка")", ResultType::Return, "Карточка" },
- { R"("position")", ResultType::Return, "position" },
- { R"("detail")", ResultType::Return, "detail" },
- { R"("actionField")", ResultType::Return, "actionField" },
- { R"("list")", ResultType::Return, "list" },
- { "\0\"", ResultType::Throw, "JSON: expected \", got \0" },
- { "\"/igrushki/konstruktory\0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/1290414/komplekt-zhenskiy-dzhemper-plusbryuki-m-254-09-malina-plustemno-siniy-\0a", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/Творчество/Рисование/Инструменты и кра\0a", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"Строительство и ремонт/Силовая техника/Зарядные устройства для автомобильных аккумуляторов/Пуско-зарядные устр\xD0\0a", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"Строительство и ремонт/Силовая техника/Зарядные устройств\xD0\0t", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"Строительство и ремонт/Силовая техника/Зарядные устройства для автомобиль\0k", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\0t", ResultType::Throw, "JSON: expected \", got \0" },
- { "\"/Хозтовары/Хранение вещей и организа\xD1\0t", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/Хозтовары/Товары для стир\0a", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"li\0a", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/734859/samolet-radioupravlyaemyy-istrebitel-rabotaet-o\0k", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/kosmetika-i-parfyum/parfyumeriya/mu\0t", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/ko\0\x04", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "", ResultType::Throw, "JSON: begin >= end." },
- { "\"/stroitelstvo-i-remont/stroit\0t", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/stroitelstvo-i-remont/stroitelnyy-instrument/av\0k", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/s\0a", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/Строительство и ремонт/Строительный инструмент/Изм\0e", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/avto/soputstvuy\0l", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/str\0\xD0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"Отвертка 2 в 1 \\\"TUNDRA basic\\\" 5х75 мм (+,-) \0\xFF", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/stroitelstvo-i-remont/stroitelnyy-instrument/avtoinstrumen\0\0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"Мелкая бытовая техника/Мелки\xD0\0\0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"Пряжа \\\"Бамбук стрейч\\0\0\0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"Карандаш чёрнографитны\xD0\0\xD0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/Творчество/Рукоделие, аппликации/Пряжа и шерсть для \xD0\0l", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/1071547/karandash-chernografitnyy-volshebstvo-nv-kruglyy-d-7-2mm-dl-176mm-plast-tuba/\0e", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"ca\0e", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"ca\0e", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/1165424/chipbord-vyrubnoy-dlya-skrapbukinga-malyshi-mikki-maus-disney-bebi\0t", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/posuda/kuhonnye-prinadlezhnosti-i-i\0d", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/Канцтовары/Ежедневники и блокн\xD0\0\0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/kanctovary/ezhednevniki-i-blok\0a", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"Стакан \xD0\0a", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"Набор бумаги для скрапбукинга \\\"Мои первый годик\\\": Микки Маус, Дисней бэби, 12 листов 29.5 х 29.5 см, 160\0\x80", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"c\0\0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"Органайзер для хранения аксессуаров, \0\0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"quantity\00", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"Сменный блок для тетрадей на кольцах А5, 160 листов клетка, офсет \xE2\x84\0=", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/Сувениры/Ф\xD0\0 ", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"\0\"", ResultType::Return, "\0" },
- { "\"\0\x04", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"va\0\0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"ca\0\0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"В \0\x04", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/letnie-tovary/z\0\x04", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"Посудомоечная машина Ha\0=", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"Крупная бытов\0\0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"Полочная акустическая система Magnat Needl\0\0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"brand\00", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"\0d", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"pos\0 ", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"c\0o", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"var\0\0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"Телевизоры и видеотехника/Всё для домашних кинотеатр\0=", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"Флеш-диск Transcend JetFlash 620 8GB (TS8GJF62\0\0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"Табурет Мег\0\xD0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"variant\0\x04", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"Катал\xD0\0\"", ResultType::Return, "Катал\xD0\0" },
- { "\"К\0\0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"Полочная акустическая система Magnat Needl\0\0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"brand\00", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"\0d", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"pos\0 ", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"c\0o", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"17\0o", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/igrushki/razvivayusc\0 ", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"Ключница \\\"\0 ", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/Игр\xD1\0 ", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/Игрушки/Игрушки для девочек/Игровые модули дл\xD1\0o", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"Крупная бытовая техника/Стиральные машины/С фронт\xD0\0o", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\0 ", ResultType::Throw, "JSON: expected \", got \0" },
- { "\"Светодиодная лента SMD3528, 5 м. IP33, 60LED, зеленый, 4,8W/мет\xD1\0 ", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"Сантехника/Мебель для ванных комнат/Стол\0o", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\0o", ResultType::Throw, "JSON: expected \", got \0" },
- { "\"/igrushki/konstruktory\0 ", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/posuda/kuhonnye-prinadlezhnosti-i-instrumenty/kuhonnye-pr\0 ", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/1290414/komplekt-zhenskiy-dzhemper-plusbryuki-m-254-09-malina-plustemno-siniy-\0o", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/Творчество/Рисование/Инструменты и кра\0o", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"Строительство и ремонт/Силовая техника/Зарядные устройства для автомобильных аккумуляторов/Пуско-зарядные устр\xD0\0o", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"Строительство и ремонт/Силовая техника/Зарядные устройств\xD0\0 ", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"Строительство и ремонт/Силовая техника/Зарядные устройства для автомобиль\0d", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\0 ", ResultType::Throw, "JSON: expected \", got \0" },
- { "\"/Хозтовары/Хранение вещей и организа\xD1\0 ", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/Хозтовары/Товары для стир\0o", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"li\0o", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/igrushki/igrus\0d", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/734859/samolet-radioupravlyaemyy-istrebitel-rabotaet-o\0 ", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/kosmetika-i-parfyum/parfyumeriya/mu\00", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/ko\0\0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/avto/avtomobilnyy\0\0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/stroitelstvo-i-remont/stroit\00", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/stroitelstvo-i-remont/stroitelnyy-instrument/av\0 ", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/s\0d", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/Строительство и ремонт/Строительный инструмент/Изм\0o", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/avto/soputstvuy\0\"", ResultType::Return, "/avto/soputstvuy\0" },
- { "\"/str\0k", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"Отвертка 2 в 1 \\\"TUNDRA basic\\\" 5х75 мм (+,-) \0\xD0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/stroitelstvo-i-remont/stroitelnyy-instrument/avtoinstrumen\0=", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"Чайник электрический Vitesse\0=", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"Мелкая бытовая техника/Мелки\xD0\0\xD0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"Пряжа \\\"Бамбук стрейч\\0о", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"Карандаш чёрнографитны\xD0\0k", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/Творчество/Рукоделие, аппликации/Пряжа и шерсть для \xD0\0\"", ResultType::Return, "/Творчество/Рукоделие, аппликации/Пряжа и шерсть для \xD0\0" },
- { "\"/1071547/karandash-chernografitnyy-volshebstvo-nv-kruglyy-d-7-2mm-dl-176mm-plast-tuba/\0o", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"ca\0o", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/Подаро\0o", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"Средство для прочис\xD1\0o", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"i\0o", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/p\0\"", ResultType::Return, "/p\0" },
- { "\"/Сувениры/Магниты, н\xD0\0k", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"Дерев\xD0\0=", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/prazdniki/svadba/svadebnaya-c\0\xD0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/Канцт\0d", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/Праздники/То\xD0\0 ", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"v\0 ", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/Косметика \xD0\0d", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/Спорт и отдых/Настольные игры/Покер, руле\xD1\0\xD0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"categ\0=", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/retailr\0k", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/retailrocket\0k", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"Ежедневник недат А5 140л кл,ляссе,обл пв\0=", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/432809/ezhednevnik-organayzer-sredniy-s-remeshkom-na-knopke-v-oblozhke-kalkulyator-kalendar-do-\0\xD0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/1165424/chipbord-vyrubnoy-dlya-skrapbukinga-malyshi-mikki-maus-disney-bebi\0d", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/posuda/kuhonnye-prinadlezhnosti-i-i\0 ", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/Канцтовары/Ежедневники и блокн\xD0\0o", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"/kanctovary/ezhednevniki-i-blok\00", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"Стакан \xD0\0\0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"Набор бумаги для скрапбукинга \\\"Мои первый годик\\\": Микки Маус, Дисней бэби, 12 листов 29.5 х 29.5 см, 160\0\0", ResultType::Throw, "JSON: incorrect syntax (expected end of string, found end of JSON)." },
- { "\"c\0\"", ResultType::Return, "c\0" },
- };
-
- for (auto i : boost::irange(0, 1/*00000*/))
- {
- static_cast(i);
-
- for (auto & r : test_data)
- {
- try
- {
- JSON j(r.input, r.input + strlen(r.input));
-
- ASSERT_EQ(j.getString(), r.result);
- ASSERT_TRUE(r.result_type == ResultType::Return);
- }
- catch (JSONException & e)
- {
- ASSERT_TRUE(r.result_type == ResultType::Throw);
- ASSERT_EQ(e.message(), r.result);
- }
- }
- }
-}
diff --git a/base/daemon/CMakeLists.txt b/base/daemon/CMakeLists.txt
index 26d59a57e7f..6ef87db6a61 100644
--- a/base/daemon/CMakeLists.txt
+++ b/base/daemon/CMakeLists.txt
@@ -5,6 +5,11 @@ add_library (daemon
)
target_include_directories (daemon PUBLIC ..)
+
+if (OS_DARWIN AND NOT MAKE_STATIC_LIBRARIES)
+ target_link_libraries (daemon PUBLIC -Wl,-undefined,dynamic_lookup)
+endif()
+
target_link_libraries (daemon PUBLIC loggers PRIVATE clickhouse_common_io clickhouse_common_config common ${EXECINFO_LIBRARIES})
if (USE_SENTRY)
diff --git a/base/daemon/SentryWriter.cpp b/base/daemon/SentryWriter.cpp
index 29430b65983..1b7d0064b99 100644
--- a/base/daemon/SentryWriter.cpp
+++ b/base/daemon/SentryWriter.cpp
@@ -9,6 +9,7 @@
#include
#include
+#include
#include
#include
#include
diff --git a/base/ext/scope_guard_safe.h b/base/ext/scope_guard_safe.h
new file mode 100644
index 00000000000..7cfb3959a81
--- /dev/null
+++ b/base/ext/scope_guard_safe.h
@@ -0,0 +1,66 @@
+#pragma once
+
+#include
+#include
+#include
+
+/// Same as SCOPE_EXIT() but block the MEMORY_LIMIT_EXCEEDED errors.
+///
+/// Typical example of SCOPE_EXIT_MEMORY() usage is when code under it may do
+/// some tiny allocations, that may fail under high memory pressure or/and low
+/// max_memory_usage (and related limits).
+///
+/// NOTE: it should be used with caution.
+#define SCOPE_EXIT_MEMORY(...) SCOPE_EXIT( \
+ MemoryTracker::LockExceptionInThread lock_memory_tracker; \
+ __VA_ARGS__; \
+)
+
+/// Same as SCOPE_EXIT() but try/catch/tryLogCurrentException any exceptions.
+///
+/// SCOPE_EXIT_SAFE() should be used in case the exception during the code
+/// under SCOPE_EXIT() is not "that fatal" and error message in log is enough.
+///
+/// Good example is calling CurrentThread::detachQueryIfNotDetached().
+///
+/// Anti-pattern is calling WriteBuffer::finalize() under SCOPE_EXIT_SAFE()
+/// (since finalize() can do final write and it is better to fail abnormally
+/// instead of ignoring write error).
+///
+/// NOTE: it should be used with double caution.
+#define SCOPE_EXIT_SAFE(...) SCOPE_EXIT( \
+ try \
+ { \
+ __VA_ARGS__; \
+ } \
+ catch (...) \
+ { \
+ tryLogCurrentException(__PRETTY_FUNCTION__); \
+ } \
+)
+
+/// Same as SCOPE_EXIT() but:
+/// - block the MEMORY_LIMIT_EXCEEDED errors,
+/// - try/catch/tryLogCurrentException any exceptions.
+///
+/// SCOPE_EXIT_MEMORY_SAFE() can be used when the error can be ignored, and in
+/// addition to SCOPE_EXIT_SAFE() it will also lock MEMORY_LIMIT_EXCEEDED to
+/// avoid such exceptions.
+///
+/// It does exists as a separate helper, since you do not need to lock
+/// MEMORY_LIMIT_EXCEEDED always (there are cases when code under SCOPE_EXIT does
+/// not do any allocations, while LockExceptionInThread increment atomic
+/// variable).
+///
+/// NOTE: it should be used with triple caution.
+#define SCOPE_EXIT_MEMORY_SAFE(...) SCOPE_EXIT( \
+ try \
+ { \
+ MemoryTracker::LockExceptionInThread lock_memory_tracker; \
+ __VA_ARGS__; \
+ } \
+ catch (...) \
+ { \
+ tryLogCurrentException(__PRETTY_FUNCTION__); \
+ } \
+)
diff --git a/base/glibc-compatibility/memcpy/memcpy.h b/base/glibc-compatibility/memcpy/memcpy.h
index f9f81bcb0fe..211d144cecb 100644
--- a/base/glibc-compatibility/memcpy/memcpy.h
+++ b/base/glibc-compatibility/memcpy/memcpy.h
@@ -178,7 +178,7 @@ tail:
size -= padding;
}
- /// Aligned unrolled copy. We will use all available SSE registers.
+ /// Aligned unrolled copy. We will use half of available SSE registers.
/// It's not possible to have both src and dst aligned.
/// So, we will use aligned stores and unaligned loads.
__m128i c0, c1, c2, c3, c4, c5, c6, c7;
diff --git a/base/mysqlxx/PoolWithFailover.cpp b/base/mysqlxx/PoolWithFailover.cpp
index 5e9f70f4ac1..ea2d060e596 100644
--- a/base/mysqlxx/PoolWithFailover.cpp
+++ b/base/mysqlxx/PoolWithFailover.cpp
@@ -2,7 +2,6 @@
#include
#include
#include
-
#include
@@ -15,9 +14,12 @@ static bool startsWith(const std::string & s, const char * prefix)
using namespace mysqlxx;
-PoolWithFailover::PoolWithFailover(const Poco::Util::AbstractConfiguration & config_,
- const std::string & config_name_, const unsigned default_connections_,
- const unsigned max_connections_, const size_t max_tries_)
+PoolWithFailover::PoolWithFailover(
+ const Poco::Util::AbstractConfiguration & config_,
+ const std::string & config_name_,
+ const unsigned default_connections_,
+ const unsigned max_connections_,
+ const size_t max_tries_)
: max_tries(max_tries_)
{
shareable = config_.getBool(config_name_ + ".share_connection", false);
@@ -59,16 +61,38 @@ PoolWithFailover::PoolWithFailover(const Poco::Util::AbstractConfiguration & con
}
}
-PoolWithFailover::PoolWithFailover(const std::string & config_name_, const unsigned default_connections_,
- const unsigned max_connections_, const size_t max_tries_)
- : PoolWithFailover{
- Poco::Util::Application::instance().config(), config_name_,
- default_connections_, max_connections_, max_tries_}
+
+PoolWithFailover::PoolWithFailover(
+ const std::string & config_name_,
+ const unsigned default_connections_,
+ const unsigned max_connections_,
+ const size_t max_tries_)
+ : PoolWithFailover{Poco::Util::Application::instance().config(),
+ config_name_, default_connections_, max_connections_, max_tries_}
{
}
+
+PoolWithFailover::PoolWithFailover(
+ const std::string & database,
+ const RemoteDescription & addresses,
+ const std::string & user,
+ const std::string & password,
+ size_t max_tries_)
+ : max_tries(max_tries_)
+ , shareable(false)
+{
+ /// Replicas have the same priority, but traversed replicas are moved to the end of the queue.
+ for (const auto & [host, port] : addresses)
+ {
+ replicas_by_priority[0].emplace_back(std::make_shared(database, host, user, password, port));
+ }
+}
+
+
PoolWithFailover::PoolWithFailover(const PoolWithFailover & other)
- : max_tries{other.max_tries}, shareable{other.shareable}
+ : max_tries{other.max_tries}
+ , shareable{other.shareable}
{
if (shareable)
{
diff --git a/base/mysqlxx/PoolWithFailover.h b/base/mysqlxx/PoolWithFailover.h
index 029fc3ebad3..5154fc3e253 100644
--- a/base/mysqlxx/PoolWithFailover.h
+++ b/base/mysqlxx/PoolWithFailover.h
@@ -11,6 +11,8 @@
namespace mysqlxx
{
/** MySQL connection pool with support of failover.
+ *
+ * For dictionary source:
* Have information about replicas and their priorities.
* Tries to connect to replica in an order of priority. When equal priority, choose replica with maximum time without connections.
*
@@ -68,42 +70,58 @@ namespace mysqlxx
using PoolPtr = std::shared_ptr;
using Replicas = std::vector;
- /// [priority][index] -> replica.
+ /// [priority][index] -> replica. Highest priority is 0.
using ReplicasByPriority = std::map;
-
ReplicasByPriority replicas_by_priority;
/// Number of connection tries.
size_t max_tries;
/// Mutex for set of replicas.
std::mutex mutex;
-
/// Can the Pool be shared
bool shareable;
public:
using Entry = Pool::Entry;
+ using RemoteDescription = std::vector>;
/**
- * config_name Name of parameter in configuration file.
+ * * Mysql dictionary sourse related params:
+ * config_name Name of parameter in configuration file for dictionary source.
+ *
+ * * Mysql storage related parameters:
+ * replicas_description
+ *
+ * * Mutual parameters:
* default_connections Number of connection in pool to each replica at start.
* max_connections Maximum number of connections in pool to each replica.
* max_tries_ Max number of connection tries.
*/
- PoolWithFailover(const std::string & config_name_,
+ PoolWithFailover(
+ const std::string & config_name_,
unsigned default_connections_ = MYSQLXX_POOL_WITH_FAILOVER_DEFAULT_START_CONNECTIONS,
unsigned max_connections_ = MYSQLXX_POOL_WITH_FAILOVER_DEFAULT_MAX_CONNECTIONS,
size_t max_tries_ = MYSQLXX_POOL_WITH_FAILOVER_DEFAULT_MAX_TRIES);
- PoolWithFailover(const Poco::Util::AbstractConfiguration & config_,
+ PoolWithFailover(
+ const Poco::Util::AbstractConfiguration & config_,
const std::string & config_name_,
unsigned default_connections_ = MYSQLXX_POOL_WITH_FAILOVER_DEFAULT_START_CONNECTIONS,
unsigned max_connections_ = MYSQLXX_POOL_WITH_FAILOVER_DEFAULT_MAX_CONNECTIONS,
size_t max_tries_ = MYSQLXX_POOL_WITH_FAILOVER_DEFAULT_MAX_TRIES);
+ PoolWithFailover(
+ const std::string & database,
+ const RemoteDescription & addresses,
+ const std::string & user,
+ const std::string & password,
+ size_t max_tries_ = MYSQLXX_POOL_WITH_FAILOVER_DEFAULT_MAX_TRIES);
+
PoolWithFailover(const PoolWithFailover & other);
/** Allocates a connection to use. */
Entry get();
};
+
+ using PoolWithFailoverPtr = std::shared_ptr;
}
diff --git a/base/mysqlxx/tests/CMakeLists.txt b/base/mysqlxx/tests/CMakeLists.txt
index 2cf19d78418..6473a927308 100644
--- a/base/mysqlxx/tests/CMakeLists.txt
+++ b/base/mysqlxx/tests/CMakeLists.txt
@@ -1,5 +1,2 @@
-add_executable (mysqlxx_test mysqlxx_test.cpp)
-target_link_libraries (mysqlxx_test PRIVATE mysqlxx)
-
add_executable (mysqlxx_pool_test mysqlxx_pool_test.cpp)
target_link_libraries (mysqlxx_pool_test PRIVATE mysqlxx)
diff --git a/base/mysqlxx/tests/failover.xml b/base/mysqlxx/tests/failover.xml
deleted file mode 100644
index 73702eabb29..00000000000
--- a/base/mysqlxx/tests/failover.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- 3306
- root
- Metrica
- qwerty
-
- example02t
- 0
-
-
- example02t
- 3306
- root
- qwerty
- Metrica
- 1
-
-
-
diff --git a/base/mysqlxx/tests/mysqlxx_test.cpp b/base/mysqlxx/tests/mysqlxx_test.cpp
deleted file mode 100644
index c505d34a58d..00000000000
--- a/base/mysqlxx/tests/mysqlxx_test.cpp
+++ /dev/null
@@ -1,77 +0,0 @@
-#include
-#include
-
-
-int main(int, char **)
-{
- try
- {
- mysqlxx::Connection connection("test", "127.0.0.1", "root", "qwerty", 3306);
- std::cerr << "Connected." << std::endl;
-
- {
- mysqlxx::Query query = connection.query();
- query << "SELECT 1 x, '2010-01-01 01:01:01' d";
- mysqlxx::UseQueryResult result = query.use();
- std::cerr << "use() called." << std::endl;
-
- while (mysqlxx::Row row = result.fetch())
- {
- std::cerr << "Fetched row." << std::endl;
- std::cerr << row[0] << ", " << row["x"] << std::endl;
- std::cerr << row[1] << ", " << row["d"]
- << ", " << row[1].getDate()
- << ", " << row[1].getDateTime()
- << ", " << row[1].getDate()
- << ", " << row[1].getDateTime()
- << std::endl
- << row[1].getDate() << ", " << row[1].getDateTime() << std::endl
- << row[1].getDate() << ", " << row[1].getDateTime() << std::endl
- << row[1].getDate() << ", " << row[1].getDateTime() << std::endl
- << row[1].getDate() << ", " << row[1].getDateTime() << std::endl
- ;
-
- time_t t1 = row[0];
- time_t t2 = row[1];
- std::cerr << t1 << ", " << LocalDateTime(t1) << std::endl;
- std::cerr << t2 << ", " << LocalDateTime(t2) << std::endl;
- }
- }
-
- {
- mysqlxx::UseQueryResult result = connection.query("SELECT 'abc\\\\def' x").use();
- mysqlxx::Row row = result.fetch();
- std::cerr << row << std::endl;
- std::cerr << row << std::endl;
- }
-
- {
- /// Копирование Query
- mysqlxx::Query query1 = connection.query("SELECT");
- mysqlxx::Query query2 = query1;
- query2 << " 1";
-
- std::cerr << query1.str() << ", " << query2.str() << std::endl;
- }
-
- {
- /// NULL
- mysqlxx::Null x = mysqlxx::null;
- std::cerr << (x == mysqlxx::null ? "Ok" : "Fail") << std::endl;
- std::cerr << (x == 0 ? "Fail" : "Ok") << std::endl;
- std::cerr << (x.isNull() ? "Ok" : "Fail") << std::endl;
- x = 1;
- std::cerr << (x == mysqlxx::null ? "Fail" : "Ok") << std::endl;
- std::cerr << (x == 0 ? "Fail" : "Ok") << std::endl;
- std::cerr << (x == 1 ? "Ok" : "Fail") << std::endl;
- std::cerr << (x.isNull() ? "Fail" : "Ok") << std::endl;
- }
- }
- catch (const mysqlxx::Exception & e)
- {
- std::cerr << e.code() << ", " << e.message() << std::endl;
- throw;
- }
-
- return 0;
-}
diff --git a/cmake/analysis.cmake b/cmake/analysis.cmake
index 369be295746..267bb34248b 100644
--- a/cmake/analysis.cmake
+++ b/cmake/analysis.cmake
@@ -16,6 +16,10 @@ if (ENABLE_CLANG_TIDY)
set (USE_CLANG_TIDY ON)
+ # clang-tidy requires assertions to guide the analysis
+ # Note that NDEBUG is set implicitly by CMake for non-debug builds
+ set(COMPILER_FLAGS "${COMPILER_FLAGS} -UNDEBUG")
+
# The variable CMAKE_CXX_CLANG_TIDY will be set inside src and base directories with non third-party code.
# set (CMAKE_CXX_CLANG_TIDY "${CLANG_TIDY_PATH}")
elseif (FAIL_ON_UNSUPPORTED_OPTIONS_COMBINATION)
diff --git a/cmake/autogenerated_versions.txt b/cmake/autogenerated_versions.txt
index bd7885bc41b..9d74179902d 100644
--- a/cmake/autogenerated_versions.txt
+++ b/cmake/autogenerated_versions.txt
@@ -1,9 +1,9 @@
# This strings autochanged from release_lib.sh:
-SET(VERSION_REVISION 54449)
+SET(VERSION_REVISION 54450)
SET(VERSION_MAJOR 21)
-SET(VERSION_MINOR 4)
+SET(VERSION_MINOR 5)
SET(VERSION_PATCH 1)
-SET(VERSION_GITHASH af2135ef9dc72f16fa4f229b731262c3f0a8bbdc)
-SET(VERSION_DESCRIBE v21.4.1.1-prestable)
-SET(VERSION_STRING 21.4.1.1)
+SET(VERSION_GITHASH 3827789b3d8fd2021952e57e5110343d26daa1a1)
+SET(VERSION_DESCRIBE v21.5.1.1-prestable)
+SET(VERSION_STRING 21.5.1.1)
# end of autochange
diff --git a/cmake/darwin/default_libs.cmake b/cmake/darwin/default_libs.cmake
index 79ac675f234..6c298106c6b 100644
--- a/cmake/darwin/default_libs.cmake
+++ b/cmake/darwin/default_libs.cmake
@@ -1,11 +1,11 @@
set (DEFAULT_LIBS "-nodefaultlibs")
-if (NOT COMPILER_CLANG)
- message (FATAL_ERROR "Darwin build is supported only for Clang")
-endif ()
-
set (DEFAULT_LIBS "${DEFAULT_LIBS} ${COVERAGE_OPTION} -lc -lm -lpthread -ldl")
+if (COMPILER_GCC)
+ set (DEFAULT_LIBS "${DEFAULT_LIBS} -lgcc_eh")
+endif ()
+
message(STATUS "Default libraries: ${DEFAULT_LIBS}")
set(CMAKE_CXX_STANDARD_LIBRARIES ${DEFAULT_LIBS})
diff --git a/cmake/find/amqpcpp.cmake b/cmake/find/amqpcpp.cmake
index 4191dce26bb..e3eaaf33ddb 100644
--- a/cmake/find/amqpcpp.cmake
+++ b/cmake/find/amqpcpp.cmake
@@ -1,3 +1,8 @@
+if (OS_DARWIN AND COMPILER_GCC)
+ # AMQP-CPP requires libuv which cannot be built with GCC in macOS due to a bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93082
+ set (ENABLE_AMQPCPP OFF CACHE INTERNAL "")
+endif()
+
option(ENABLE_AMQPCPP "Enalbe AMQP-CPP" ${ENABLE_LIBRARIES})
if (NOT ENABLE_AMQPCPP)
diff --git a/cmake/find/base64.cmake b/cmake/find/base64.cmake
index 7427baf9cad..acade11eb2f 100644
--- a/cmake/find/base64.cmake
+++ b/cmake/find/base64.cmake
@@ -1,4 +1,8 @@
-option (ENABLE_BASE64 "Enable base64" ${ENABLE_LIBRARIES})
+if(ARCH_AMD64 OR ARCH_ARM)
+ option (ENABLE_BASE64 "Enable base64" ${ENABLE_LIBRARIES})
+elseif(ENABLE_BASE64)
+ message (${RECONFIGURE_MESSAGE_LEVEL} "base64 library is only supported on x86_64 and aarch64")
+endif()
if (NOT ENABLE_BASE64)
return()
diff --git a/cmake/find/cassandra.cmake b/cmake/find/cassandra.cmake
index 037d6c3f131..ded25a5bf41 100644
--- a/cmake/find/cassandra.cmake
+++ b/cmake/find/cassandra.cmake
@@ -1,3 +1,8 @@
+if (OS_DARWIN AND COMPILER_GCC)
+ # Cassandra requires libuv which cannot be built with GCC in macOS due to a bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93082
+ set (ENABLE_CASSANDRA OFF CACHE INTERNAL "")
+endif()
+
option(ENABLE_CASSANDRA "Enable Cassandra" ${ENABLE_LIBRARIES})
if (NOT ENABLE_CASSANDRA)
diff --git a/cmake/find/ccache.cmake b/cmake/find/ccache.cmake
index fea1f8b4c97..986c9cb5fe2 100644
--- a/cmake/find/ccache.cmake
+++ b/cmake/find/ccache.cmake
@@ -32,7 +32,9 @@ if (CCACHE_FOUND AND NOT COMPILER_MATCHES_CCACHE)
if (CCACHE_VERSION VERSION_GREATER "3.2.0" OR NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
message(STATUS "Using ${CCACHE_FOUND} ${CCACHE_VERSION}")
- set_property (GLOBAL PROPERTY RULE_LAUNCH_COMPILE ${CCACHE_FOUND})
+ set (CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE_FOUND} ${CMAKE_CXX_COMPILER_LAUNCHER})
+ set (CMAKE_C_COMPILER_LAUNCHER ${CCACHE_FOUND} ${CMAKE_C_COMPILER_LAUNCHER})
+
set_property (GLOBAL PROPERTY RULE_LAUNCH_LINK ${CCACHE_FOUND})
# debian (debhelpers) set SOURCE_DATE_EPOCH environment variable, that is
diff --git a/cmake/find/datasketches.cmake b/cmake/find/datasketches.cmake
new file mode 100644
index 00000000000..44ef324a9f2
--- /dev/null
+++ b/cmake/find/datasketches.cmake
@@ -0,0 +1,29 @@
+option (ENABLE_DATASKETCHES "Enable DataSketches" ${ENABLE_LIBRARIES})
+
+if (ENABLE_DATASKETCHES)
+
+option (USE_INTERNAL_DATASKETCHES_LIBRARY "Set to FALSE to use system DataSketches library instead of bundled" ${NOT_UNBUNDLED})
+
+if (NOT EXISTS "${ClickHouse_SOURCE_DIR}/contrib/datasketches-cpp/theta/CMakeLists.txt")
+ if (USE_INTERNAL_DATASKETCHES_LIBRARY)
+ message(WARNING "submodule contrib/datasketches-cpp is missing. to fix try run: \n git submodule update --init --recursive")
+ endif()
+ set(MISSING_INTERNAL_DATASKETCHES_LIBRARY 1)
+ set(USE_INTERNAL_DATASKETCHES_LIBRARY 0)
+endif()
+
+if (USE_INTERNAL_DATASKETCHES_LIBRARY)
+ set(DATASKETCHES_LIBRARY theta)
+ set(DATASKETCHES_INCLUDE_DIR "${ClickHouse_SOURCE_DIR}/contrib/datasketches-cpp/common/include" "${ClickHouse_SOURCE_DIR}/contrib/datasketches-cpp/theta/include")
+elseif (NOT MISSING_INTERNAL_DATASKETCHES_LIBRARY)
+ find_library(DATASKETCHES_LIBRARY theta)
+ find_path(DATASKETCHES_INCLUDE_DIR NAMES theta_sketch.hpp PATHS ${DATASKETCHES_INCLUDE_PATHS})
+endif()
+
+if (DATASKETCHES_LIBRARY AND DATASKETCHES_INCLUDE_DIR)
+ set(USE_DATASKETCHES 1)
+endif()
+
+endif()
+
+message (STATUS "Using datasketches=${USE_DATASKETCHES}: ${DATASKETCHES_INCLUDE_DIR} : ${DATASKETCHES_LIBRARY}")
diff --git a/cmake/find/fastops.cmake b/cmake/find/fastops.cmake
index 5ab320bdb7a..1675646654e 100644
--- a/cmake/find/fastops.cmake
+++ b/cmake/find/fastops.cmake
@@ -1,7 +1,7 @@
-if(NOT ARCH_ARM AND NOT OS_FREEBSD AND NOT OS_DARWIN)
+if(ARCH_AMD64 AND NOT OS_FREEBSD AND NOT OS_DARWIN)
option(ENABLE_FASTOPS "Enable fast vectorized mathematical functions library by Mikhail Parakhin" ${ENABLE_LIBRARIES})
elseif(ENABLE_FASTOPS)
- message (${RECONFIGURE_MESSAGE_LEVEL} "Fastops library is not supported on ARM, FreeBSD and Darwin")
+ message (${RECONFIGURE_MESSAGE_LEVEL} "Fastops library is supported on x86_64 only, and not FreeBSD or Darwin")
endif()
if(NOT ENABLE_FASTOPS)
diff --git a/cmake/find/hdfs3.cmake b/cmake/find/hdfs3.cmake
index 7b385f24e1e..3aab2b612ef 100644
--- a/cmake/find/hdfs3.cmake
+++ b/cmake/find/hdfs3.cmake
@@ -1,4 +1,4 @@
-if(NOT ARCH_ARM AND NOT OS_FREEBSD AND NOT APPLE AND USE_PROTOBUF)
+if(NOT ARCH_ARM AND NOT OS_FREEBSD AND NOT APPLE AND USE_PROTOBUF AND NOT ARCH_PPC64LE)
option(ENABLE_HDFS "Enable HDFS" ${ENABLE_LIBRARIES})
elseif(ENABLE_HDFS OR USE_INTERNAL_HDFS3_LIBRARY)
message (${RECONFIGURE_MESSAGE_LEVEL} "Cannot use HDFS3 with current configuration")
diff --git a/cmake/find/ldap.cmake b/cmake/find/ldap.cmake
index 369c1e42e8d..0dffa334e73 100644
--- a/cmake/find/ldap.cmake
+++ b/cmake/find/ldap.cmake
@@ -62,6 +62,7 @@ if (NOT OPENLDAP_FOUND AND NOT MISSING_INTERNAL_LDAP_LIBRARY)
if (
( "${_system_name}" STREQUAL "linux" AND "${_system_processor}" STREQUAL "x86_64" ) OR
( "${_system_name}" STREQUAL "linux" AND "${_system_processor}" STREQUAL "aarch64" ) OR
+ ( "${_system_name}" STREQUAL "linux" AND "${_system_processor}" STREQUAL "ppc64le" ) OR
( "${_system_name}" STREQUAL "freebsd" AND "${_system_processor}" STREQUAL "x86_64" ) OR
( "${_system_name}" STREQUAL "darwin" AND "${_system_processor}" STREQUAL "x86_64" )
)
diff --git a/cmake/find/nanodbc.cmake b/cmake/find/nanodbc.cmake
new file mode 100644
index 00000000000..2c913abb13e
--- /dev/null
+++ b/cmake/find/nanodbc.cmake
@@ -0,0 +1,35 @@
+option(ENABLE_NANODBC "Enalbe nanodbc" ${ENABLE_LIBRARIES})
+
+if (NOT ENABLE_NANODBC)
+ set (USE_ODBC 0)
+ return()
+endif()
+
+if (NOT ENABLE_ODBC)
+ set (USE_NANODBC 0)
+ message (STATUS "Using nanodbc=${USE_NANODBC}")
+ return()
+endif()
+
+if (NOT EXISTS "${ClickHouse_SOURCE_DIR}/contrib/nanodbc/CMakeLists.txt")
+ message (WARNING "submodule contrib/nanodbc is missing. to fix try run: \n git submodule update --init --recursive")
+ message (${RECONFIGURE_MESSAGE_LEVEL} "Can't find internal nanodbc library")
+ set (USE_NANODBC 0)
+ return()
+endif()
+
+if (NOT EXISTS "${ClickHouse_SOURCE_DIR}/contrib/unixodbc/include")
+ message (ERROR "submodule contrib/unixodbc is missing. to fix try run: \n git submodule update --init --recursive")
+ message (${RECONFIGURE_MESSAGE_LEVEL} "Can't find internal unixodbc needed for nanodbc")
+ set (USE_NANODBC 0)
+ return()
+endif()
+
+set (USE_NANODBC 1)
+
+set (NANODBC_LIBRARY nanodbc)
+
+set (NANODBC_INCLUDE_DIR "${ClickHouse_SOURCE_DIR}/contrib/nanodbc/nanodbce")
+
+message (STATUS "Using nanodbc=${USE_NANODBC}: ${NANODBC_INCLUDE_DIR} : ${NANODBC_LIBRARY}")
+message (STATUS "Using unixodbc")
diff --git a/cmake/find/nuraft.cmake b/cmake/find/nuraft.cmake
index 7fa5251946e..4e5258e132f 100644
--- a/cmake/find/nuraft.cmake
+++ b/cmake/find/nuraft.cmake
@@ -11,7 +11,7 @@ if (NOT EXISTS "${ClickHouse_SOURCE_DIR}/contrib/NuRaft/CMakeLists.txt")
return()
endif ()
-if (NOT OS_FREEBSD AND NOT OS_DARWIN)
+if (NOT OS_FREEBSD)
set (USE_NURAFT 1)
set (NURAFT_LIBRARY nuraft)
diff --git a/cmake/find/s3.cmake b/cmake/find/s3.cmake
index 1bbf48fd6b0..1b0c652a31a 100644
--- a/cmake/find/s3.cmake
+++ b/cmake/find/s3.cmake
@@ -1,7 +1,7 @@
-if(NOT OS_FREEBSD AND NOT APPLE AND NOT ARCH_ARM)
+if(NOT OS_FREEBSD AND NOT APPLE)
option(ENABLE_S3 "Enable S3" ${ENABLE_LIBRARIES})
elseif(ENABLE_S3 OR USE_INTERNAL_AWS_S3_LIBRARY)
- message (${RECONFIGURE_MESSAGE_LEVEL} "Can't use S3 on ARM, Apple or FreeBSD")
+ message (${RECONFIGURE_MESSAGE_LEVEL} "Can't use S3 on Apple or FreeBSD")
endif()
if(NOT ENABLE_S3)
diff --git a/cmake/find/xz.cmake b/cmake/find/xz.cmake
new file mode 100644
index 00000000000..0d19859c6b1
--- /dev/null
+++ b/cmake/find/xz.cmake
@@ -0,0 +1,27 @@
+option (USE_INTERNAL_XZ_LIBRARY "Set to OFF to use system xz (lzma) library instead of bundled" ${NOT_UNBUNDLED})
+
+if(NOT EXISTS "${ClickHouse_SOURCE_DIR}/contrib/xz/src/liblzma/api/lzma.h")
+ if(USE_INTERNAL_XZ_LIBRARY)
+ message(WARNING "submodule contrib/xz is missing. to fix try run: \n git submodule update --init --recursive")
+ message (${RECONFIGURE_MESSAGE_LEVEL} "Can't find internal xz (lzma) library")
+ set(USE_INTERNAL_XZ_LIBRARY 0)
+ endif()
+ set(MISSING_INTERNAL_XZ_LIBRARY 1)
+endif()
+
+if (NOT USE_INTERNAL_XZ_LIBRARY)
+ find_library (XZ_LIBRARY lzma)
+ find_path (XZ_INCLUDE_DIR NAMES lzma.h PATHS ${XZ_INCLUDE_PATHS})
+ if (NOT XZ_LIBRARY OR NOT XZ_INCLUDE_DIR)
+ message (${RECONFIGURE_MESSAGE_LEVEL} "Can't find system xz (lzma) library")
+ endif ()
+endif ()
+
+if (XZ_LIBRARY AND XZ_INCLUDE_DIR)
+elseif (NOT MISSING_INTERNAL_XZ_LIBRARY)
+ set (USE_INTERNAL_XZ_LIBRARY 1)
+ set (XZ_LIBRARY liblzma)
+ set (XZ_INCLUDE_DIR ${ClickHouse_SOURCE_DIR}/contrib/xz/src/liblzma/api)
+endif ()
+
+message (STATUS "Using xz (lzma): ${XZ_INCLUDE_DIR} : ${XZ_LIBRARY}")
diff --git a/cmake/linux/default_libs.cmake b/cmake/linux/default_libs.cmake
index d3a727e9cb8..c1e4d450389 100644
--- a/cmake/linux/default_libs.cmake
+++ b/cmake/linux/default_libs.cmake
@@ -6,7 +6,7 @@ set (DEFAULT_LIBS "-nodefaultlibs")
# We need builtins from Clang's RT even without libcxx - for ubsan+int128.
# See https://bugs.llvm.org/show_bug.cgi?id=16404
if (COMPILER_CLANG AND NOT (CMAKE_CROSSCOMPILING AND ARCH_AARCH64))
- execute_process (COMMAND ${CMAKE_CXX_COMPILER} --print-file-name=libclang_rt.builtins-${CMAKE_SYSTEM_PROCESSOR}.a OUTPUT_VARIABLE BUILTINS_LIBRARY OUTPUT_STRIP_TRAILING_WHITESPACE)
+ execute_process (COMMAND ${CMAKE_CXX_COMPILER} --print-libgcc-file-name --rtlib=compiler-rt OUTPUT_VARIABLE BUILTINS_LIBRARY OUTPUT_STRIP_TRAILING_WHITESPACE)
else ()
set (BUILTINS_LIBRARY "-lgcc")
endif ()
diff --git a/cmake/tools.cmake b/cmake/tools.cmake
index abb11843d59..44fc3b3e530 100644
--- a/cmake/tools.cmake
+++ b/cmake/tools.cmake
@@ -86,8 +86,3 @@ if (LINKER_NAME)
message(STATUS "Using custom linker by name: ${LINKER_NAME}")
endif ()
-if (ARCH_PPC64LE)
- if (COMPILER_CLANG OR (COMPILER_GCC AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 8))
- message(FATAL_ERROR "Only gcc-8 or higher is supported for powerpc architecture")
- endif ()
-endif ()
diff --git a/cmake/warnings.cmake b/cmake/warnings.cmake
index 8122e9ef31e..a398c59e981 100644
--- a/cmake/warnings.cmake
+++ b/cmake/warnings.cmake
@@ -11,11 +11,6 @@ if (NOT MSVC)
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wextra")
endif ()
-if (USE_DEBUG_HELPERS)
- set (INCLUDE_DEBUG_HELPERS "-I${ClickHouse_SOURCE_DIR}/base -include ${ClickHouse_SOURCE_DIR}/src/Core/iostream_debug_helpers.h")
- set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${INCLUDE_DEBUG_HELPERS}")
-endif ()
-
# Add some warnings that are not available even with -Wall -Wextra -Wpedantic.
# Intended for exploration of new compiler warnings that may be found useful.
# Applies to clang only
diff --git a/contrib/CMakeLists.txt b/contrib/CMakeLists.txt
index 6a56b4cc733..d05177739fe 100644
--- a/contrib/CMakeLists.txt
+++ b/contrib/CMakeLists.txt
@@ -47,7 +47,10 @@ add_subdirectory (lz4-cmake)
add_subdirectory (murmurhash)
add_subdirectory (replxx-cmake)
add_subdirectory (unixodbc-cmake)
-add_subdirectory (xz)
+
+if (USE_INTERNAL_XZ_LIBRARY)
+ add_subdirectory (xz)
+endif()
add_subdirectory (poco-cmake)
add_subdirectory (croaring-cmake)
@@ -215,15 +218,17 @@ if (USE_EMBEDDED_COMPILER AND USE_INTERNAL_LLVM_LIBRARY)
set (LLVM_ENABLE_RTTI 1 CACHE INTERNAL "")
set (LLVM_ENABLE_PIC 0 CACHE INTERNAL "")
set (LLVM_TARGETS_TO_BUILD "X86;AArch64" CACHE STRING "")
- # Yes it is set globally, but this is not enough, since llvm will add -std=c++11 after default
- # And c++2a cannot be used, due to ambiguous operator !=
- if (COMPILER_GCC OR COMPILER_CLANG)
- set (_CXX_STANDARD "gnu++17")
- else()
- set (_CXX_STANDARD "c++17")
- endif()
- set (LLVM_CXX_STD ${_CXX_STANDARD} CACHE STRING "" FORCE)
+
+ # Need to use C++17 since the compilation is not possible with C++20 currently, due to ambiguous operator != etc.
+ # LLVM project will set its default value for the -std=... but our global setting from CMake will override it.
+ set (CMAKE_CXX_STANDARD_bak ${CMAKE_CXX_STANDARD})
+ set (CMAKE_CXX_STANDARD 17)
+
add_subdirectory (llvm/llvm)
+
+ set (CMAKE_CXX_STANDARD ${CMAKE_CXX_STANDARD_bak})
+ unset (CMAKE_CXX_STANDARD_bak)
+
target_include_directories(LLVMSupport SYSTEM BEFORE PRIVATE ${ZLIB_INCLUDE_DIR})
endif ()
@@ -280,7 +285,14 @@ if (USE_AMQPCPP)
add_subdirectory (amqpcpp-cmake)
endif()
if (USE_CASSANDRA)
+ # Need to use C++17 since the compilation is not possible with C++20 currently.
+ set (CMAKE_CXX_STANDARD_bak ${CMAKE_CXX_STANDARD})
+ set (CMAKE_CXX_STANDARD 17)
+
add_subdirectory (cassandra)
+
+ set (CMAKE_CXX_STANDARD ${CMAKE_CXX_STANDARD_bak})
+ unset (CMAKE_CXX_STANDARD_bak)
endif()
# Should go before:
@@ -314,6 +326,10 @@ if (USE_LIBPQXX)
add_subdirectory (libpqxx-cmake)
endif()
+if (USE_NANODBC)
+ add_subdirectory (nanodbc-cmake)
+endif()
+
if (USE_NURAFT)
add_subdirectory(nuraft-cmake)
endif()
diff --git a/contrib/NuRaft b/contrib/NuRaft
index 3d3683e7775..d2feb5978b9 160000
--- a/contrib/NuRaft
+++ b/contrib/NuRaft
@@ -1 +1 @@
-Subproject commit 3d3683e77753cfe015a05fae95ddf418e19f59e1
+Subproject commit d2feb5978b979729a07c3ca76eaa4ab94cef4ceb
diff --git a/contrib/antlr4-runtime b/contrib/antlr4-runtime
index a2fa7b76e2e..672643e9a42 160000
--- a/contrib/antlr4-runtime
+++ b/contrib/antlr4-runtime
@@ -1 +1 @@
-Subproject commit a2fa7b76e2ee16d2ad955e9214a90bbf79da66fc
+Subproject commit 672643e9a427ef803abf13bc8cb4989606553d64
diff --git a/contrib/arrow b/contrib/arrow
index 744bdfe188f..616b3dc76a0 160000
--- a/contrib/arrow
+++ b/contrib/arrow
@@ -1 +1 @@
-Subproject commit 744bdfe188f018e5e05f5deebd4e9ee0a7706cf4
+Subproject commit 616b3dc76a0c8450b4027ded8a78e9619d7c845f
diff --git a/contrib/boost-cmake/CMakeLists.txt b/contrib/boost-cmake/CMakeLists.txt
index b9298f59f2b..0759935a7db 100644
--- a/contrib/boost-cmake/CMakeLists.txt
+++ b/contrib/boost-cmake/CMakeLists.txt
@@ -160,6 +160,12 @@ if (NOT EXTERNAL_BOOST_FOUND)
enable_language(ASM)
SET(ASM_OPTIONS "-x assembler-with-cpp")
+ set (SRCS_CONTEXT
+ ${LIBRARY_DIR}/libs/context/src/dummy.cpp
+ ${LIBRARY_DIR}/libs/context/src/execution_context.cpp
+ ${LIBRARY_DIR}/libs/context/src/posix/stack_traits.cpp
+ )
+
if (SANITIZE AND (SANITIZE STREQUAL "address" OR SANITIZE STREQUAL "thread"))
add_compile_definitions(BOOST_USE_UCONTEXT)
@@ -169,39 +175,34 @@ if (NOT EXTERNAL_BOOST_FOUND)
add_compile_definitions(BOOST_USE_TSAN)
endif()
- set (SRCS_CONTEXT
+ set (SRCS_CONTEXT ${SRCS_CONTEXT}
${LIBRARY_DIR}/libs/context/src/fiber.cpp
${LIBRARY_DIR}/libs/context/src/continuation.cpp
- ${LIBRARY_DIR}/libs/context/src/dummy.cpp
- ${LIBRARY_DIR}/libs/context/src/execution_context.cpp
- ${LIBRARY_DIR}/libs/context/src/posix/stack_traits.cpp
)
- elseif (ARCH_ARM)
- set (SRCS_CONTEXT
+ endif()
+ if (ARCH_ARM)
+ set (SRCS_CONTEXT ${SRCS_CONTEXT}
${LIBRARY_DIR}/libs/context/src/asm/jump_arm64_aapcs_elf_gas.S
${LIBRARY_DIR}/libs/context/src/asm/make_arm64_aapcs_elf_gas.S
${LIBRARY_DIR}/libs/context/src/asm/ontop_arm64_aapcs_elf_gas.S
- ${LIBRARY_DIR}/libs/context/src/dummy.cpp
- ${LIBRARY_DIR}/libs/context/src/execution_context.cpp
- ${LIBRARY_DIR}/libs/context/src/posix/stack_traits.cpp
+ )
+ elseif (ARCH_PPC64LE)
+ set (SRCS_CONTEXT ${SRCS_CONTEXT}
+ ${LIBRARY_DIR}/libs/context/src/asm/jump_ppc64_sysv_elf_gas.S
+ ${LIBRARY_DIR}/libs/context/src/asm/make_ppc64_sysv_elf_gas.S
+ ${LIBRARY_DIR}/libs/context/src/asm/ontop_ppc64_sysv_elf_gas.S
)
elseif(OS_DARWIN)
- set (SRCS_CONTEXT
+ set (SRCS_CONTEXT ${SRCS_CONTEXT}
${LIBRARY_DIR}/libs/context/src/asm/jump_x86_64_sysv_macho_gas.S
${LIBRARY_DIR}/libs/context/src/asm/make_x86_64_sysv_macho_gas.S
${LIBRARY_DIR}/libs/context/src/asm/ontop_x86_64_sysv_macho_gas.S
- ${LIBRARY_DIR}/libs/context/src/dummy.cpp
- ${LIBRARY_DIR}/libs/context/src/execution_context.cpp
- ${LIBRARY_DIR}/libs/context/src/posix/stack_traits.cpp
)
else()
- set (SRCS_CONTEXT
+ set (SRCS_CONTEXT ${SRCS_CONTEXT}
${LIBRARY_DIR}/libs/context/src/asm/jump_x86_64_sysv_elf_gas.S
${LIBRARY_DIR}/libs/context/src/asm/make_x86_64_sysv_elf_gas.S
${LIBRARY_DIR}/libs/context/src/asm/ontop_x86_64_sysv_elf_gas.S
- ${LIBRARY_DIR}/libs/context/src/dummy.cpp
- ${LIBRARY_DIR}/libs/context/src/execution_context.cpp
- ${LIBRARY_DIR}/libs/context/src/posix/stack_traits.cpp
)
endif()
diff --git a/contrib/boringssl b/contrib/boringssl
index fd9ce1a0406..83c1cda8a02 160000
--- a/contrib/boringssl
+++ b/contrib/boringssl
@@ -1 +1 @@
-Subproject commit fd9ce1a0406f571507068b9555d0b545b8a18332
+Subproject commit 83c1cda8a0224dc817cbad2966c7ed4acc35f02a
diff --git a/contrib/boringssl-cmake/CMakeLists.txt b/contrib/boringssl-cmake/CMakeLists.txt
index 017a8a64c0e..adfee82dda4 100644
--- a/contrib/boringssl-cmake/CMakeLists.txt
+++ b/contrib/boringssl-cmake/CMakeLists.txt
@@ -16,7 +16,7 @@ endif()
if(CMAKE_COMPILER_IS_GNUCXX OR CLANG)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fvisibility=hidden -fno-common -fno-exceptions -fno-rtti")
- if(APPLE)
+ if(APPLE AND CLANG)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
endif()
diff --git a/contrib/cctz-cmake/CMakeLists.txt b/contrib/cctz-cmake/CMakeLists.txt
index 90e33dc9f62..a3869478347 100644
--- a/contrib/cctz-cmake/CMakeLists.txt
+++ b/contrib/cctz-cmake/CMakeLists.txt
@@ -97,12 +97,19 @@ if (NOT EXTERNAL_CCTZ_LIBRARY_FOUND OR NOT EXTERNAL_CCTZ_LIBRARY_WORKS)
set(TZ_OBJS ${TZ_OBJS} ${TZ_OBJ})
# https://stackoverflow.com/questions/14776463/compile-and-add-an-object-file-from-a-binary-with-cmake
- add_custom_command(OUTPUT ${TZ_OBJ}
- COMMAND cp ${TZDIR}/${TIMEZONE} ${CMAKE_CURRENT_BINARY_DIR}/${TIMEZONE_ID}
- COMMAND cd ${CMAKE_CURRENT_BINARY_DIR} && ${OBJCOPY_PATH} -I binary ${OBJCOPY_ARCH_OPTIONS}
+ # PPC64LE fails to do this with objcopy, use ld or lld instead
+ if (ARCH_PPC64LE)
+ add_custom_command(OUTPUT ${TZ_OBJ}
+ COMMAND cp ${TZDIR}/${TIMEZONE} ${CMAKE_CURRENT_BINARY_DIR}/${TIMEZONE_ID}
+ COMMAND cd ${CMAKE_CURRENT_BINARY_DIR} && ${CMAKE_LINKER} -m elf64lppc -r -b binary -o ${TZ_OBJ} ${TIMEZONE_ID}
+ COMMAND rm ${CMAKE_CURRENT_BINARY_DIR}/${TIMEZONE_ID})
+ else()
+ add_custom_command(OUTPUT ${TZ_OBJ}
+ COMMAND cp ${TZDIR}/${TIMEZONE} ${CMAKE_CURRENT_BINARY_DIR}/${TIMEZONE_ID}
+ COMMAND cd ${CMAKE_CURRENT_BINARY_DIR} && ${OBJCOPY_PATH} -I binary ${OBJCOPY_ARCH_OPTIONS}
--rename-section .data=.rodata,alloc,load,readonly,data,contents ${TIMEZONE_ID} ${TZ_OBJ}
- COMMAND rm ${CMAKE_CURRENT_BINARY_DIR}/${TIMEZONE_ID})
-
+ COMMAND rm ${CMAKE_CURRENT_BINARY_DIR}/${TIMEZONE_ID})
+ endif()
set_source_files_properties(${TZ_OBJ} PROPERTIES EXTERNAL_OBJECT true GENERATED true)
endforeach(TIMEZONE)
diff --git a/contrib/datasketches-cpp b/contrib/datasketches-cpp
new file mode 160000
index 00000000000..f915d35b2de
--- /dev/null
+++ b/contrib/datasketches-cpp
@@ -0,0 +1 @@
+Subproject commit f915d35b2de676683493c86c585141a1e1c83334
diff --git a/contrib/flatbuffers b/contrib/flatbuffers
index 6df40a24717..22e3ffc66d2 160000
--- a/contrib/flatbuffers
+++ b/contrib/flatbuffers
@@ -1 +1 @@
-Subproject commit 6df40a2471737b27271bdd9b900ab5f3aec746c7
+Subproject commit 22e3ffc66d2d7d72d1414390aa0f04ffd114a5a1
diff --git a/contrib/grpc b/contrib/grpc
index 7436366ceb3..8d558f03fe3 160000
--- a/contrib/grpc
+++ b/contrib/grpc
@@ -1 +1 @@
-Subproject commit 7436366ceb341ba5c00ea29f1645e02a2b70bf93
+Subproject commit 8d558f03fe370240081424fafa76cdc9301ea14b
diff --git a/contrib/jemalloc-cmake/CMakeLists.txt b/contrib/jemalloc-cmake/CMakeLists.txt
index b8a6474413a..b174d4d361e 100644
--- a/contrib/jemalloc-cmake/CMakeLists.txt
+++ b/contrib/jemalloc-cmake/CMakeLists.txt
@@ -1,7 +1,7 @@
-if (SANITIZE OR NOT (ARCH_AMD64 OR ARCH_ARM) OR NOT (OS_LINUX OR OS_FREEBSD OR OS_DARWIN))
+if (SANITIZE OR NOT (ARCH_AMD64 OR ARCH_ARM OR ARCH_PPC64LE) OR NOT (OS_LINUX OR OS_FREEBSD OR OS_DARWIN))
if (ENABLE_JEMALLOC)
message (${RECONFIGURE_MESSAGE_LEVEL}
- "jemalloc is disabled implicitly: it doesn't work with sanitizers and can only be used with x86_64 or aarch64 on linux or freebsd.")
+ "jemalloc is disabled implicitly: it doesn't work with sanitizers and can only be used with x86_64, aarch64 or ppc64le on linux or freebsd.")
endif()
set (ENABLE_JEMALLOC OFF)
else()
@@ -107,6 +107,8 @@ if (ARCH_AMD64)
set(JEMALLOC_INCLUDE_PREFIX "${JEMALLOC_INCLUDE_PREFIX}_x86_64")
elseif (ARCH_ARM)
set(JEMALLOC_INCLUDE_PREFIX "${JEMALLOC_INCLUDE_PREFIX}_aarch64")
+elseif (ARCH_PPC64LE)
+ set(JEMALLOC_INCLUDE_PREFIX "${JEMALLOC_INCLUDE_PREFIX}_ppc64le")
else ()
message (FATAL_ERROR "internal jemalloc: This arch is not supported")
endif ()
@@ -119,12 +121,14 @@ target_include_directories(jemalloc SYSTEM PRIVATE
target_compile_definitions(jemalloc PRIVATE -DJEMALLOC_NO_PRIVATE_NAMESPACE)
if (CMAKE_BUILD_TYPE_UC STREQUAL "DEBUG")
- target_compile_definitions(jemalloc PRIVATE -DJEMALLOC_DEBUG=1 -DJEMALLOC_PROF=1)
+ target_compile_definitions(jemalloc PRIVATE -DJEMALLOC_DEBUG=1)
+endif ()
- if (USE_UNWIND)
- target_compile_definitions (jemalloc PRIVATE -DJEMALLOC_PROF_LIBUNWIND=1)
- target_link_libraries (jemalloc PRIVATE unwind)
- endif ()
+target_compile_definitions(jemalloc PRIVATE -DJEMALLOC_PROF=1)
+
+if (USE_UNWIND)
+ target_compile_definitions (jemalloc PRIVATE -DJEMALLOC_PROF_LIBUNWIND=1)
+ target_link_libraries (jemalloc PRIVATE unwind)
endif ()
target_compile_options(jemalloc PRIVATE -Wno-redundant-decls)
diff --git a/contrib/jemalloc-cmake/include_linux_ppc64le/jemalloc/internal/jemalloc_internal_defs.h.in b/contrib/jemalloc-cmake/include_linux_ppc64le/jemalloc/internal/jemalloc_internal_defs.h.in
new file mode 100644
index 00000000000..8068861041f
--- /dev/null
+++ b/contrib/jemalloc-cmake/include_linux_ppc64le/jemalloc/internal/jemalloc_internal_defs.h.in
@@ -0,0 +1,367 @@
+/* include/jemalloc/internal/jemalloc_internal_defs.h. Generated from jemalloc_internal_defs.h.in by configure. */
+#ifndef JEMALLOC_INTERNAL_DEFS_H_
+#define JEMALLOC_INTERNAL_DEFS_H_
+/*
+ * If JEMALLOC_PREFIX is defined via --with-jemalloc-prefix, it will cause all
+ * public APIs to be prefixed. This makes it possible, with some care, to use
+ * multiple allocators simultaneously.
+ */
+/* #undef JEMALLOC_PREFIX */
+/* #undef JEMALLOC_CPREFIX */
+
+/*
+ * Define overrides for non-standard allocator-related functions if they are
+ * present on the system.
+ */
+#define JEMALLOC_OVERRIDE___LIBC_CALLOC
+#define JEMALLOC_OVERRIDE___LIBC_FREE
+#define JEMALLOC_OVERRIDE___LIBC_MALLOC
+#define JEMALLOC_OVERRIDE___LIBC_MEMALIGN
+#define JEMALLOC_OVERRIDE___LIBC_REALLOC
+#define JEMALLOC_OVERRIDE___LIBC_VALLOC
+/* #undef JEMALLOC_OVERRIDE___POSIX_MEMALIGN */
+
+/*
+ * JEMALLOC_PRIVATE_NAMESPACE is used as a prefix for all library-private APIs.
+ * For shared libraries, symbol visibility mechanisms prevent these symbols
+ * from being exported, but for static libraries, naming collisions are a real
+ * possibility.
+ */
+#define JEMALLOC_PRIVATE_NAMESPACE je_
+
+/*
+ * Hyper-threaded CPUs may need a special instruction inside spin loops in
+ * order to yield to another virtual CPU.
+ */
+#define CPU_SPINWAIT
+/* 1 if CPU_SPINWAIT is defined, 0 otherwise. */
+#define HAVE_CPU_SPINWAIT 0
+
+/*
+ * Number of significant bits in virtual addresses. This may be less than the
+ * total number of bits in a pointer, e.g. on x64, for which the uppermost 16
+ * bits are the same as bit 47.
+ */
+#define LG_VADDR 64
+
+/* Defined if C11 atomics are available. */
+#define JEMALLOC_C11_ATOMICS 1
+
+/* Defined if GCC __atomic atomics are available. */
+#define JEMALLOC_GCC_ATOMIC_ATOMICS 1
+/* and the 8-bit variant support. */
+#define JEMALLOC_GCC_U8_ATOMIC_ATOMICS 1
+
+/* Defined if GCC __sync atomics are available. */
+#define JEMALLOC_GCC_SYNC_ATOMICS 1
+/* and the 8-bit variant support. */
+#define JEMALLOC_GCC_U8_SYNC_ATOMICS 1
+
+/*
+ * Defined if __builtin_clz() and __builtin_clzl() are available.
+ */
+#define JEMALLOC_HAVE_BUILTIN_CLZ
+
+/*
+ * Defined if os_unfair_lock_*() functions are available, as provided by Darwin.
+ */
+/* #undef JEMALLOC_OS_UNFAIR_LOCK */
+
+/* Defined if syscall(2) is usable. */
+#define JEMALLOC_USE_SYSCALL
+
+/*
+ * Defined if secure_getenv(3) is available.
+ */
+// #define JEMALLOC_HAVE_SECURE_GETENV
+
+/*
+ * Defined if issetugid(2) is available.
+ */
+/* #undef JEMALLOC_HAVE_ISSETUGID */
+
+/* Defined if pthread_atfork(3) is available. */
+#define JEMALLOC_HAVE_PTHREAD_ATFORK
+
+/* Defined if pthread_setname_np(3) is available. */
+#define JEMALLOC_HAVE_PTHREAD_SETNAME_NP
+
+/*
+ * Defined if clock_gettime(CLOCK_MONOTONIC_COARSE, ...) is available.
+ */
+#define JEMALLOC_HAVE_CLOCK_MONOTONIC_COARSE 1
+
+/*
+ * Defined if clock_gettime(CLOCK_MONOTONIC, ...) is available.
+ */
+#define JEMALLOC_HAVE_CLOCK_MONOTONIC 1
+
+/*
+ * Defined if mach_absolute_time() is available.
+ */
+/* #undef JEMALLOC_HAVE_MACH_ABSOLUTE_TIME */
+
+/*
+ * Defined if _malloc_thread_cleanup() exists. At least in the case of
+ * FreeBSD, pthread_key_create() allocates, which if used during malloc
+ * bootstrapping will cause recursion into the pthreads library. Therefore, if
+ * _malloc_thread_cleanup() exists, use it as the basis for thread cleanup in
+ * malloc_tsd.
+ */
+/* #undef JEMALLOC_MALLOC_THREAD_CLEANUP */
+
+/*
+ * Defined if threaded initialization is known to be safe on this platform.
+ * Among other things, it must be possible to initialize a mutex without
+ * triggering allocation in order for threaded allocation to be safe.
+ */
+#define JEMALLOC_THREADED_INIT
+
+/*
+ * Defined if the pthreads implementation defines
+ * _pthread_mutex_init_calloc_cb(), in which case the function is used in order
+ * to avoid recursive allocation during mutex initialization.
+ */
+/* #undef JEMALLOC_MUTEX_INIT_CB */
+
+/* Non-empty if the tls_model attribute is supported. */
+#define JEMALLOC_TLS_MODEL __attribute__((tls_model("initial-exec")))
+
+/*
+ * JEMALLOC_DEBUG enables assertions and other sanity checks, and disables
+ * inline functions.
+ */
+/* #undef JEMALLOC_DEBUG */
+
+/* JEMALLOC_STATS enables statistics calculation. */
+#define JEMALLOC_STATS
+
+/* JEMALLOC_EXPERIMENTAL_SMALLOCX_API enables experimental smallocx API. */
+/* #undef JEMALLOC_EXPERIMENTAL_SMALLOCX_API */
+
+/* JEMALLOC_PROF enables allocation profiling. */
+/* #undef JEMALLOC_PROF */
+
+/* Use libunwind for profile backtracing if defined. */
+/* #undef JEMALLOC_PROF_LIBUNWIND */
+
+/* Use libgcc for profile backtracing if defined. */
+/* #undef JEMALLOC_PROF_LIBGCC */
+
+/* Use gcc intrinsics for profile backtracing if defined. */
+/* #undef JEMALLOC_PROF_GCC */
+
+/*
+ * JEMALLOC_DSS enables use of sbrk(2) to allocate extents from the data storage
+ * segment (DSS).
+ */
+#define JEMALLOC_DSS
+
+/* Support memory filling (junk/zero). */
+#define JEMALLOC_FILL
+
+/* Support utrace(2)-based tracing. */
+/* #undef JEMALLOC_UTRACE */
+
+/* Support optional abort() on OOM. */
+/* #undef JEMALLOC_XMALLOC */
+
+/* Support lazy locking (avoid locking unless a second thread is launched). */
+/* #undef JEMALLOC_LAZY_LOCK */
+
+/*
+ * Minimum allocation alignment is 2^LG_QUANTUM bytes (ignoring tiny size
+ * classes).
+ */
+/* #undef LG_QUANTUM */
+
+/* One page is 2^LG_PAGE bytes. */
+#define LG_PAGE 16
+
+/*
+ * One huge page is 2^LG_HUGEPAGE bytes. Note that this is defined even if the
+ * system does not explicitly support huge pages; system calls that require
+ * explicit huge page support are separately configured.
+ */
+#define LG_HUGEPAGE 21
+
+/*
+ * If defined, adjacent virtual memory mappings with identical attributes
+ * automatically coalesce, and they fragment when changes are made to subranges.
+ * This is the normal order of things for mmap()/munmap(), but on Windows
+ * VirtualAlloc()/VirtualFree() operations must be precisely matched, i.e.
+ * mappings do *not* coalesce/fragment.
+ */
+#define JEMALLOC_MAPS_COALESCE
+
+/*
+ * If defined, retain memory for later reuse by default rather than using e.g.
+ * munmap() to unmap freed extents. This is enabled on 64-bit Linux because
+ * common sequences of mmap()/munmap() calls will cause virtual memory map
+ * holes.
+ */
+#define JEMALLOC_RETAIN
+
+/* TLS is used to map arenas and magazine caches to threads. */
+#define JEMALLOC_TLS
+
+/*
+ * Used to mark unreachable code to quiet "end of non-void" compiler warnings.
+ * Don't use this directly; instead use unreachable() from util.h
+ */
+#define JEMALLOC_INTERNAL_UNREACHABLE __builtin_unreachable
+
+/*
+ * ffs*() functions to use for bitmapping. Don't use these directly; instead,
+ * use ffs_*() from util.h.
+ */
+#define JEMALLOC_INTERNAL_FFSLL __builtin_ffsll
+#define JEMALLOC_INTERNAL_FFSL __builtin_ffsl
+#define JEMALLOC_INTERNAL_FFS __builtin_ffs
+
+/*
+ * popcount*() functions to use for bitmapping.
+ */
+#define JEMALLOC_INTERNAL_POPCOUNTL __builtin_popcountl
+#define JEMALLOC_INTERNAL_POPCOUNT __builtin_popcount
+
+/*
+ * If defined, explicitly attempt to more uniformly distribute large allocation
+ * pointer alignments across all cache indices.
+ */
+#define JEMALLOC_CACHE_OBLIVIOUS
+
+/*
+ * If defined, enable logging facilities. We make this a configure option to
+ * avoid taking extra branches everywhere.
+ */
+/* #undef JEMALLOC_LOG */
+
+/*
+ * If defined, use readlinkat() (instead of readlink()) to follow
+ * /etc/malloc_conf.
+ */
+/* #undef JEMALLOC_READLINKAT */
+
+/*
+ * Darwin (OS X) uses zones to work around Mach-O symbol override shortcomings.
+ */
+/* #undef JEMALLOC_ZONE */
+
+/*
+ * Methods for determining whether the OS overcommits.
+ * JEMALLOC_PROC_SYS_VM_OVERCOMMIT_MEMORY: Linux's
+ * /proc/sys/vm.overcommit_memory file.
+ * JEMALLOC_SYSCTL_VM_OVERCOMMIT: FreeBSD's vm.overcommit sysctl.
+ */
+/* #undef JEMALLOC_SYSCTL_VM_OVERCOMMIT */
+#define JEMALLOC_PROC_SYS_VM_OVERCOMMIT_MEMORY
+
+/* Defined if madvise(2) is available. */
+#define JEMALLOC_HAVE_MADVISE
+
+/*
+ * Defined if transparent huge pages are supported via the MADV_[NO]HUGEPAGE
+ * arguments to madvise(2).
+ */
+#define JEMALLOC_HAVE_MADVISE_HUGE
+
+/*
+ * Methods for purging unused pages differ between operating systems.
+ *
+ * madvise(..., MADV_FREE) : This marks pages as being unused, such that they
+ * will be discarded rather than swapped out.
+ * madvise(..., MADV_DONTNEED) : If JEMALLOC_PURGE_MADVISE_DONTNEED_ZEROS is
+ * defined, this immediately discards pages,
+ * such that new pages will be demand-zeroed if
+ * the address region is later touched;
+ * otherwise this behaves similarly to
+ * MADV_FREE, though typically with higher
+ * system overhead.
+ */
+#define JEMALLOC_PURGE_MADVISE_FREE
+#define JEMALLOC_PURGE_MADVISE_DONTNEED
+#define JEMALLOC_PURGE_MADVISE_DONTNEED_ZEROS
+
+/* Defined if madvise(2) is available but MADV_FREE is not (x86 Linux only). */
+/* #undef JEMALLOC_DEFINE_MADVISE_FREE */
+
+/*
+ * Defined if MADV_DO[NT]DUMP is supported as an argument to madvise.
+ */
+#define JEMALLOC_MADVISE_DONTDUMP
+
+/*
+ * Defined if transparent huge pages (THPs) are supported via the
+ * MADV_[NO]HUGEPAGE arguments to madvise(2), and THP support is enabled.
+ */
+/* #undef JEMALLOC_THP */
+
+/* Define if operating system has alloca.h header. */
+#define JEMALLOC_HAS_ALLOCA_H 1
+
+/* C99 restrict keyword supported. */
+#define JEMALLOC_HAS_RESTRICT 1
+
+/* For use by hash code. */
+/* #undef JEMALLOC_BIG_ENDIAN */
+
+/* sizeof(int) == 2^LG_SIZEOF_INT. */
+#define LG_SIZEOF_INT 2
+
+/* sizeof(long) == 2^LG_SIZEOF_LONG. */
+#define LG_SIZEOF_LONG 3
+
+/* sizeof(long long) == 2^LG_SIZEOF_LONG_LONG. */
+#define LG_SIZEOF_LONG_LONG 3
+
+/* sizeof(intmax_t) == 2^LG_SIZEOF_INTMAX_T. */
+#define LG_SIZEOF_INTMAX_T 3
+
+/* glibc malloc hooks (__malloc_hook, __realloc_hook, __free_hook). */
+#define JEMALLOC_GLIBC_MALLOC_HOOK
+
+/* glibc memalign hook. */
+#define JEMALLOC_GLIBC_MEMALIGN_HOOK
+
+/* pthread support */
+#define JEMALLOC_HAVE_PTHREAD
+
+/* dlsym() support */
+#define JEMALLOC_HAVE_DLSYM
+
+/* Adaptive mutex support in pthreads. */
+#define JEMALLOC_HAVE_PTHREAD_MUTEX_ADAPTIVE_NP
+
+/* GNU specific sched_getcpu support */
+#define JEMALLOC_HAVE_SCHED_GETCPU
+
+/* GNU specific sched_setaffinity support */
+#define JEMALLOC_HAVE_SCHED_SETAFFINITY
+
+/*
+ * If defined, all the features necessary for background threads are present.
+ */
+#define JEMALLOC_BACKGROUND_THREAD 1
+
+/*
+ * If defined, jemalloc symbols are not exported (doesn't work when
+ * JEMALLOC_PREFIX is not defined).
+ */
+/* #undef JEMALLOC_EXPORT */
+
+/* config.malloc_conf options string. */
+#define JEMALLOC_CONFIG_MALLOC_CONF "@JEMALLOC_CONFIG_MALLOC_CONF@"
+
+/* If defined, jemalloc takes the malloc/free/etc. symbol names. */
+#define JEMALLOC_IS_MALLOC 1
+
+/*
+ * Defined if strerror_r returns char * if _GNU_SOURCE is defined.
+ */
+#define JEMALLOC_STRERROR_R_RETURNS_CHAR_WITH_GNU_SOURCE
+
+/* Performs additional safety checks when defined. */
+/* #undef JEMALLOC_OPT_SAFETY_CHECKS */
+
+#endif /* JEMALLOC_INTERNAL_DEFS_H_ */
diff --git a/contrib/libcpuid-cmake/CMakeLists.txt b/contrib/libcpuid-cmake/CMakeLists.txt
index 8c1be50b4e6..9baebb3ba1b 100644
--- a/contrib/libcpuid-cmake/CMakeLists.txt
+++ b/contrib/libcpuid-cmake/CMakeLists.txt
@@ -1,11 +1,9 @@
-if (NOT ARCH_ARM)
+if(ARCH_AMD64)
option (ENABLE_CPUID "Enable libcpuid library (only internal)" ${ENABLE_LIBRARIES})
-endif()
-
-if (ARCH_ARM AND ENABLE_CPUID)
- message (${RECONFIGURE_MESSAGE_LEVEL} "cpuid is not supported on ARM")
+elseif(ENABLE_CPUID)
+ message (${RECONFIGURE_MESSAGE_LEVEL} "libcpuid is only supported on x86_64")
set (ENABLE_CPUID 0)
-endif ()
+endif()
if (NOT ENABLE_CPUID)
add_library (cpuid INTERFACE)
diff --git a/contrib/libcxx b/contrib/libcxx
index 8b80a151d12..2fa892f69ac 160000
--- a/contrib/libcxx
+++ b/contrib/libcxx
@@ -1 +1 @@
-Subproject commit 8b80a151d12b98ffe2d0c22f7cec12c3b9ff88d7
+Subproject commit 2fa892f69acbaa40f8a18c6484854a6183a34482
diff --git a/contrib/libcxx-cmake/CMakeLists.txt b/contrib/libcxx-cmake/CMakeLists.txt
index 3b5d53cd1c0..59d23b2cd9e 100644
--- a/contrib/libcxx-cmake/CMakeLists.txt
+++ b/contrib/libcxx-cmake/CMakeLists.txt
@@ -56,6 +56,11 @@ if (USE_UNWIND)
target_compile_definitions(cxx PUBLIC -DSTD_EXCEPTION_HAS_STACK_TRACE=1)
endif ()
+# Override the deduced attribute support that causes error.
+if (OS_DARWIN AND COMPILER_GCC)
+ add_compile_definitions(_LIBCPP_INIT_PRIORITY_MAX)
+endif ()
+
target_compile_options(cxx PUBLIC $<$:-nostdinc++>)
# Third party library may have substandard code.
diff --git a/contrib/libdivide/libdivide.h b/contrib/libdivide/libdivide.h
index 81057b7b43d..33d210310a1 100644
--- a/contrib/libdivide/libdivide.h
+++ b/contrib/libdivide/libdivide.h
@@ -18,78 +18,79 @@
#include
#if defined(__cplusplus)
- #include
- #include
- #include
+#include
+#include
+#include
#else
- #include
- #include
+#include
+#include
#endif
-#if defined(LIBDIVIDE_AVX512)
- #include
-#elif defined(LIBDIVIDE_AVX2)
- #include
-#elif defined(LIBDIVIDE_SSE2)
- #include
+#if defined(LIBDIVIDE_SSE2)
+#include
+#endif
+#if defined(LIBDIVIDE_AVX2) || defined(LIBDIVIDE_AVX512)
+#include
+#endif
+#if defined(LIBDIVIDE_NEON)
+#include
#endif
#if defined(_MSC_VER)
- #include
- // disable warning C4146: unary minus operator applied
- // to unsigned type, result still unsigned
- #pragma warning(disable: 4146)
- #define LIBDIVIDE_VC
+#include
+// disable warning C4146: unary minus operator applied
+// to unsigned type, result still unsigned
+#pragma warning(disable : 4146)
+#define LIBDIVIDE_VC
#endif
#if !defined(__has_builtin)
- #define __has_builtin(x) 0
+#define __has_builtin(x) 0
#endif
#if defined(__SIZEOF_INT128__)
- #define HAS_INT128_T
- // clang-cl on Windows does not yet support 128-bit division
- #if !(defined(__clang__) && defined(LIBDIVIDE_VC))
- #define HAS_INT128_DIV
- #endif
+#define HAS_INT128_T
+// clang-cl on Windows does not yet support 128-bit division
+#if !(defined(__clang__) && defined(LIBDIVIDE_VC))
+#define HAS_INT128_DIV
+#endif
#endif
#if defined(__x86_64__) || defined(_M_X64)
- #define LIBDIVIDE_X86_64
+#define LIBDIVIDE_X86_64
#endif
#if defined(__i386__)
- #define LIBDIVIDE_i386
+#define LIBDIVIDE_i386
#endif
#if defined(__GNUC__) || defined(__clang__)
- #define LIBDIVIDE_GCC_STYLE_ASM
+#define LIBDIVIDE_GCC_STYLE_ASM
#endif
#if defined(__cplusplus) || defined(LIBDIVIDE_VC)
- #define LIBDIVIDE_FUNCTION __FUNCTION__
+#define LIBDIVIDE_FUNCTION __FUNCTION__
#else
- #define LIBDIVIDE_FUNCTION __func__
+#define LIBDIVIDE_FUNCTION __func__
#endif
-#define LIBDIVIDE_ERROR(msg) \
- do { \
- fprintf(stderr, "libdivide.h:%d: %s(): Error: %s\n", \
- __LINE__, LIBDIVIDE_FUNCTION, msg); \
- abort(); \
+#define LIBDIVIDE_ERROR(msg) \
+ do { \
+ fprintf(stderr, "libdivide.h:%d: %s(): Error: %s\n", __LINE__, LIBDIVIDE_FUNCTION, msg); \
+ abort(); \
} while (0)
#if defined(LIBDIVIDE_ASSERTIONS_ON)
- #define LIBDIVIDE_ASSERT(x) \
- do { \
- if (!(x)) { \
- fprintf(stderr, "libdivide.h:%d: %s(): Assertion failed: %s\n", \
- __LINE__, LIBDIVIDE_FUNCTION, #x); \
- abort(); \
- } \
- } while (0)
+#define LIBDIVIDE_ASSERT(x) \
+ do { \
+ if (!(x)) { \
+ fprintf(stderr, "libdivide.h:%d: %s(): Assertion failed: %s\n", __LINE__, \
+ LIBDIVIDE_FUNCTION, #x); \
+ abort(); \
+ } \
+ } while (0)
#else
- #define LIBDIVIDE_ASSERT(x)
+#define LIBDIVIDE_ASSERT(x)
#endif
#ifdef __cplusplus
@@ -193,25 +194,33 @@ static inline struct libdivide_u32_branchfree_t libdivide_u32_branchfree_gen(uin
static inline struct libdivide_s64_branchfree_t libdivide_s64_branchfree_gen(int64_t d);
static inline struct libdivide_u64_branchfree_t libdivide_u64_branchfree_gen(uint64_t d);
-static inline int32_t libdivide_s32_do(int32_t numer, const struct libdivide_s32_t *denom);
+static inline int32_t libdivide_s32_do(int32_t numer, const struct libdivide_s32_t *denom);
static inline uint32_t libdivide_u32_do(uint32_t numer, const struct libdivide_u32_t *denom);
-static inline int64_t libdivide_s64_do(int64_t numer, const struct libdivide_s64_t *denom);
+static inline int64_t libdivide_s64_do(int64_t numer, const struct libdivide_s64_t *denom);
static inline uint64_t libdivide_u64_do(uint64_t numer, const struct libdivide_u64_t *denom);
-static inline int32_t libdivide_s32_branchfree_do(int32_t numer, const struct libdivide_s32_branchfree_t *denom);
-static inline uint32_t libdivide_u32_branchfree_do(uint32_t numer, const struct libdivide_u32_branchfree_t *denom);
-static inline int64_t libdivide_s64_branchfree_do(int64_t numer, const struct libdivide_s64_branchfree_t *denom);
-static inline uint64_t libdivide_u64_branchfree_do(uint64_t numer, const struct libdivide_u64_branchfree_t *denom);
+static inline int32_t libdivide_s32_branchfree_do(
+ int32_t numer, const struct libdivide_s32_branchfree_t *denom);
+static inline uint32_t libdivide_u32_branchfree_do(
+ uint32_t numer, const struct libdivide_u32_branchfree_t *denom);
+static inline int64_t libdivide_s64_branchfree_do(
+ int64_t numer, const struct libdivide_s64_branchfree_t *denom);
+static inline uint64_t libdivide_u64_branchfree_do(
+ uint64_t numer, const struct libdivide_u64_branchfree_t *denom);
-static inline int32_t libdivide_s32_recover(const struct libdivide_s32_t *denom);
+static inline int32_t libdivide_s32_recover(const struct libdivide_s32_t *denom);
static inline uint32_t libdivide_u32_recover(const struct libdivide_u32_t *denom);
-static inline int64_t libdivide_s64_recover(const struct libdivide_s64_t *denom);
+static inline int64_t libdivide_s64_recover(const struct libdivide_s64_t *denom);
static inline uint64_t libdivide_u64_recover(const struct libdivide_u64_t *denom);
-static inline int32_t libdivide_s32_branchfree_recover(const struct libdivide_s32_branchfree_t *denom);
-static inline uint32_t libdivide_u32_branchfree_recover(const struct libdivide_u32_branchfree_t *denom);
-static inline int64_t libdivide_s64_branchfree_recover(const struct libdivide_s64_branchfree_t *denom);
-static inline uint64_t libdivide_u64_branchfree_recover(const struct libdivide_u64_branchfree_t *denom);
+static inline int32_t libdivide_s32_branchfree_recover(
+ const struct libdivide_s32_branchfree_t *denom);
+static inline uint32_t libdivide_u32_branchfree_recover(
+ const struct libdivide_u32_branchfree_t *denom);
+static inline int64_t libdivide_s64_branchfree_recover(
+ const struct libdivide_s64_branchfree_t *denom);
+static inline uint64_t libdivide_u64_branchfree_recover(
+ const struct libdivide_u64_branchfree_t *denom);
//////// Internal Utility Functions
@@ -229,8 +238,7 @@ static inline int32_t libdivide_mullhi_s32(int32_t x, int32_t y) {
}
static inline uint64_t libdivide_mullhi_u64(uint64_t x, uint64_t y) {
-#if defined(LIBDIVIDE_VC) && \
- defined(LIBDIVIDE_X86_64)
+#if defined(LIBDIVIDE_VC) && defined(LIBDIVIDE_X86_64)
return __umulh(x, y);
#elif defined(HAS_INT128_T)
__uint128_t xl = x, yl = y;
@@ -256,8 +264,7 @@ static inline uint64_t libdivide_mullhi_u64(uint64_t x, uint64_t y) {
}
static inline int64_t libdivide_mullhi_s64(int64_t x, int64_t y) {
-#if defined(LIBDIVIDE_VC) && \
- defined(LIBDIVIDE_X86_64)
+#if defined(LIBDIVIDE_VC) && defined(LIBDIVIDE_X86_64)
return __mulh(x, y);
#elif defined(HAS_INT128_T)
__int128_t xl = x, yl = y;
@@ -279,8 +286,7 @@ static inline int64_t libdivide_mullhi_s64(int64_t x, int64_t y) {
}
static inline int32_t libdivide_count_leading_zeros32(uint32_t val) {
-#if defined(__GNUC__) || \
- __has_builtin(__builtin_clz)
+#if defined(__GNUC__) || __has_builtin(__builtin_clz)
// Fast way to count leading zeros
return __builtin_clz(val);
#elif defined(LIBDIVIDE_VC)
@@ -290,8 +296,7 @@ static inline int32_t libdivide_count_leading_zeros32(uint32_t val) {
}
return 0;
#else
- if (val == 0)
- return 32;
+ if (val == 0) return 32;
int32_t result = 8;
uint32_t hi = 0xFFU << 24;
while ((val & hi) == 0) {
@@ -307,8 +312,7 @@ static inline int32_t libdivide_count_leading_zeros32(uint32_t val) {
}
static inline int32_t libdivide_count_leading_zeros64(uint64_t val) {
-#if defined(__GNUC__) || \
- __has_builtin(__builtin_clzll)
+#if defined(__GNUC__) || __has_builtin(__builtin_clzll)
// Fast way to count leading zeros
return __builtin_clzll(val);
#elif defined(LIBDIVIDE_VC) && defined(_WIN64)
@@ -328,14 +332,11 @@ static inline int32_t libdivide_count_leading_zeros64(uint64_t val) {
// libdivide_64_div_32_to_32: divides a 64-bit uint {u1, u0} by a 32-bit
// uint {v}. The result must fit in 32 bits.
// Returns the quotient directly and the remainder in *r
-static inline uint32_t libdivide_64_div_32_to_32(uint32_t u1, uint32_t u0, uint32_t v, uint32_t *r) {
-#if (defined(LIBDIVIDE_i386) || defined(LIBDIVIDE_X86_64)) && \
- defined(LIBDIVIDE_GCC_STYLE_ASM)
+static inline uint32_t libdivide_64_div_32_to_32(
+ uint32_t u1, uint32_t u0, uint32_t v, uint32_t *r) {
+#if (defined(LIBDIVIDE_i386) || defined(LIBDIVIDE_X86_64)) && defined(LIBDIVIDE_GCC_STYLE_ASM)
uint32_t result;
- __asm__("divl %[v]"
- : "=a"(result), "=d"(*r)
- : [v] "r"(v), "a"(u0), "d"(u1)
- );
+ __asm__("divl %[v]" : "=a"(result), "=d"(*r) : [v] "r"(v), "a"(u0), "d"(u1));
return result;
#else
uint64_t n = ((uint64_t)u1 << 32) | u0;
@@ -349,19 +350,13 @@ static inline uint32_t libdivide_64_div_32_to_32(uint32_t u1, uint32_t u0, uint3
// uint {v}. The result must fit in 64 bits.
// Returns the quotient directly and the remainder in *r
static uint64_t libdivide_128_div_64_to_64(uint64_t u1, uint64_t u0, uint64_t v, uint64_t *r) {
-#if defined(LIBDIVIDE_X86_64) && \
- defined(LIBDIVIDE_GCC_STYLE_ASM)
+ // N.B. resist the temptation to use __uint128_t here.
+ // In LLVM compiler-rt, it performs a 128/128 -> 128 division which is many times slower than
+ // necessary. In gcc it's better but still slower than the divlu implementation, perhaps because
+ // it's not inlined.
+#if defined(LIBDIVIDE_X86_64) && defined(LIBDIVIDE_GCC_STYLE_ASM)
uint64_t result;
- __asm__("divq %[v]"
- : "=a"(result), "=d"(*r)
- : [v] "r"(v), "a"(u0), "d"(u1)
- );
- return result;
-#elif defined(HAS_INT128_T) && \
- defined(HAS_INT128_DIV)
- __uint128_t n = ((__uint128_t)u1 << 64) | u0;
- uint64_t result = (uint64_t)(n / v);
- *r = (uint64_t)(n - result * (__uint128_t)v);
+ __asm__("divq %[v]" : "=a"(result), "=d"(*r) : [v] "r"(v), "a"(u0), "d"(u1));
return result;
#else
// Code taken from Hacker's Delight:
@@ -369,19 +364,19 @@ static uint64_t libdivide_128_div_64_to_64(uint64_t u1, uint64_t u0, uint64_t v,
// License permits inclusion here per:
// http://www.hackersdelight.org/permissions.htm
- const uint64_t b = (1ULL << 32); // Number base (32 bits)
- uint64_t un1, un0; // Norm. dividend LSD's
- uint64_t vn1, vn0; // Norm. divisor digits
- uint64_t q1, q0; // Quotient digits
- uint64_t un64, un21, un10; // Dividend digit pairs
- uint64_t rhat; // A remainder
- int32_t s; // Shift amount for norm
+ const uint64_t b = (1ULL << 32); // Number base (32 bits)
+ uint64_t un1, un0; // Norm. dividend LSD's
+ uint64_t vn1, vn0; // Norm. divisor digits
+ uint64_t q1, q0; // Quotient digits
+ uint64_t un64, un21, un10; // Dividend digit pairs
+ uint64_t rhat; // A remainder
+ int32_t s; // Shift amount for norm
// If overflow, set rem. to an impossible value,
// and return the largest possible quotient
if (u1 >= v) {
- *r = (uint64_t) -1;
- return (uint64_t) -1;
+ *r = (uint64_t)-1;
+ return (uint64_t)-1;
}
// count leading zeros
@@ -390,7 +385,7 @@ static uint64_t libdivide_128_div_64_to_64(uint64_t u1, uint64_t u0, uint64_t v,
// Normalize divisor
v = v << s;
un64 = (u1 << s) | (u0 >> (64 - s));
- un10 = u0 << s; // Shift dividend left
+ un10 = u0 << s; // Shift dividend left
} else {
// Avoid undefined behavior of (u0 >> 64).
// The behavior is undefined if the right operand is
@@ -415,11 +410,10 @@ static uint64_t libdivide_128_div_64_to_64(uint64_t u1, uint64_t u0, uint64_t v,
while (q1 >= b || q1 * vn0 > b * rhat + un1) {
q1 = q1 - 1;
rhat = rhat + vn1;
- if (rhat >= b)
- break;
+ if (rhat >= b) break;
}
- // Multiply and subtract
+ // Multiply and subtract
un21 = un64 * b + un1 - q1 * v;
// Compute the second quotient digit
@@ -429,8 +423,7 @@ static uint64_t libdivide_128_div_64_to_64(uint64_t u1, uint64_t u0, uint64_t v,
while (q0 >= b || q0 * vn0 > b * rhat + un0) {
q0 = q0 - 1;
rhat = rhat + vn1;
- if (rhat >= b)
- break;
+ if (rhat >= b) break;
}
*r = (un21 * b + un0 - q0 * v) >> s;
@@ -445,8 +438,7 @@ static inline void libdivide_u128_shift(uint64_t *u1, uint64_t *u0, int32_t sign
*u1 <<= shift;
*u1 |= *u0 >> (64 - shift);
*u0 <<= shift;
- }
- else if (signed_shift < 0) {
+ } else if (signed_shift < 0) {
uint32_t shift = -signed_shift;
*u0 >>= shift;
*u0 |= *u1 << (64 - shift);
@@ -455,9 +447,9 @@ static inline void libdivide_u128_shift(uint64_t *u1, uint64_t *u0, int32_t sign
}
// Computes a 128 / 128 -> 64 bit division, with a 128 bit remainder.
-static uint64_t libdivide_128_div_128_to_64(uint64_t u_hi, uint64_t u_lo, uint64_t v_hi, uint64_t v_lo, uint64_t *r_hi, uint64_t *r_lo) {
-#if defined(HAS_INT128_T) && \
- defined(HAS_INT128_DIV)
+static uint64_t libdivide_128_div_128_to_64(
+ uint64_t u_hi, uint64_t u_lo, uint64_t v_hi, uint64_t v_lo, uint64_t *r_hi, uint64_t *r_lo) {
+#if defined(HAS_INT128_T) && defined(HAS_INT128_DIV)
__uint128_t ufull = u_hi;
__uint128_t vfull = v_hi;
ufull = (ufull << 64) | u_lo;
@@ -470,7 +462,10 @@ static uint64_t libdivide_128_div_128_to_64(uint64_t u_hi, uint64_t u_lo, uint64
#else
// Adapted from "Unsigned Doubleword Division" in Hacker's Delight
// We want to compute u / v
- typedef struct { uint64_t hi; uint64_t lo; } u128_t;
+ typedef struct {
+ uint64_t hi;
+ uint64_t lo;
+ } u128_t;
u128_t u = {u_hi, u_lo};
u128_t v = {v_hi, v_lo};
@@ -490,7 +485,7 @@ static uint64_t libdivide_128_div_128_to_64(uint64_t u_hi, uint64_t u_lo, uint64
// Normalize the divisor so its MSB is 1
u128_t v1t = v;
libdivide_u128_shift(&v1t.hi, &v1t.lo, n);
- uint64_t v1 = v1t.hi; // i.e. v1 = v1t >> 64
+ uint64_t v1 = v1t.hi; // i.e. v1 = v1t >> 64
// To ensure no overflow
u128_t u1 = u;
@@ -508,7 +503,7 @@ static uint64_t libdivide_128_div_128_to_64(uint64_t u_hi, uint64_t u_lo, uint64
// Make q0 correct or too small by 1
// Equivalent to `if (q0 != 0) q0 = q0 - 1;`
if (q0.hi != 0 || q0.lo != 0) {
- q0.hi -= (q0.lo == 0); // borrow
+ q0.hi -= (q0.lo == 0); // borrow
q0.lo -= 1;
}
@@ -520,22 +515,21 @@ static uint64_t libdivide_128_div_128_to_64(uint64_t u_hi, uint64_t u_lo, uint64
// Each term is 128 bit
// High half of full product (upper 128 bits!) are dropped
u128_t q0v = {0, 0};
- q0v.hi = q0.hi*v.lo + q0.lo*v.hi + libdivide_mullhi_u64(q0.lo, v.lo);
- q0v.lo = q0.lo*v.lo;
+ q0v.hi = q0.hi * v.lo + q0.lo * v.hi + libdivide_mullhi_u64(q0.lo, v.lo);
+ q0v.lo = q0.lo * v.lo;
// Compute u - q0v as u_q0v
// This is the remainder
u128_t u_q0v = u;
- u_q0v.hi -= q0v.hi + (u.lo < q0v.lo); // second term is borrow
+ u_q0v.hi -= q0v.hi + (u.lo < q0v.lo); // second term is borrow
u_q0v.lo -= q0v.lo;
// Check if u_q0v >= v
// This checks if our remainder is larger than the divisor
- if ((u_q0v.hi > v.hi) ||
- (u_q0v.hi == v.hi && u_q0v.lo >= v.lo)) {
+ if ((u_q0v.hi > v.hi) || (u_q0v.hi == v.hi && u_q0v.lo >= v.lo)) {
// Increment q0
q0.lo += 1;
- q0.hi += (q0.lo == 0); // carry
+ q0.hi += (q0.lo == 0); // carry
// Subtract v from remainder
u_q0v.hi -= v.hi + (u_q0v.lo < v.lo);
@@ -611,7 +605,8 @@ struct libdivide_u32_branchfree_t libdivide_u32_branchfree_gen(uint32_t d) {
LIBDIVIDE_ERROR("branchfree divider must be != 1");
}
struct libdivide_u32_t tmp = libdivide_internal_u32_gen(d, 1);
- struct libdivide_u32_branchfree_t ret = {tmp.magic, (uint8_t)(tmp.more & LIBDIVIDE_32_SHIFT_MASK)};
+ struct libdivide_u32_branchfree_t ret = {
+ tmp.magic, (uint8_t)(tmp.more & LIBDIVIDE_32_SHIFT_MASK)};
return ret;
}
@@ -619,14 +614,12 @@ uint32_t libdivide_u32_do(uint32_t numer, const struct libdivide_u32_t *denom) {
uint8_t more = denom->more;
if (!denom->magic) {
return numer >> more;
- }
- else {
+ } else {
uint32_t q = libdivide_mullhi_u32(denom->magic, numer);
if (more & LIBDIVIDE_ADD_MARKER) {
uint32_t t = ((numer - q) >> 1) + q;
return t >> (more & LIBDIVIDE_32_SHIFT_MASK);
- }
- else {
+ } else {
// All upper bits are 0,
// don't need to mask them off.
return q >> more;
@@ -634,7 +627,8 @@ uint32_t libdivide_u32_do(uint32_t numer, const struct libdivide_u32_t *denom) {
}
}
-uint32_t libdivide_u32_branchfree_do(uint32_t numer, const struct libdivide_u32_branchfree_t *denom) {
+uint32_t libdivide_u32_branchfree_do(
+ uint32_t numer, const struct libdivide_u32_branchfree_t *denom) {
uint32_t q = libdivide_mullhi_u32(denom->magic, numer);
uint32_t t = ((numer - q) >> 1) + q;
return t >> denom->more;
@@ -671,7 +665,7 @@ uint32_t libdivide_u32_recover(const struct libdivide_u32_t *denom) {
// Need to double it, and then add 1 to the quotient if doubling th
// remainder would increase the quotient.
// Note that rem<<1 cannot overflow, since rem < d and d is 33 bits
- uint32_t full_q = half_q + half_q + ((rem<<1) >= d);
+ uint32_t full_q = half_q + half_q + ((rem << 1) >= d);
// We rounded down in gen (hence +1)
return full_q + 1;
@@ -700,7 +694,7 @@ uint32_t libdivide_u32_branchfree_recover(const struct libdivide_u32_branchfree_
// Need to double it, and then add 1 to the quotient if doubling th
// remainder would increase the quotient.
// Note that rem<<1 cannot overflow, since rem < d and d is 33 bits
- uint32_t full_q = half_q + half_q + ((rem<<1) >= d);
+ uint32_t full_q = half_q + half_q + ((rem << 1) >= d);
// We rounded down in gen (hence +1)
return full_q + 1;
@@ -747,7 +741,7 @@ static inline struct libdivide_u64_t libdivide_internal_u64_gen(uint64_t d, int
proposed_m += proposed_m;
const uint64_t twice_rem = rem + rem;
if (twice_rem >= d || twice_rem < rem) proposed_m += 1;
- more = floor_log_2_d | LIBDIVIDE_ADD_MARKER;
+ more = floor_log_2_d | LIBDIVIDE_ADD_MARKER;
}
result.magic = 1 + proposed_m;
result.more = more;
@@ -770,7 +764,8 @@ struct libdivide_u64_branchfree_t libdivide_u64_branchfree_gen(uint64_t d) {
LIBDIVIDE_ERROR("branchfree divider must be != 1");
}
struct libdivide_u64_t tmp = libdivide_internal_u64_gen(d, 1);
- struct libdivide_u64_branchfree_t ret = {tmp.magic, (uint8_t)(tmp.more & LIBDIVIDE_64_SHIFT_MASK)};
+ struct libdivide_u64_branchfree_t ret = {
+ tmp.magic, (uint8_t)(tmp.more & LIBDIVIDE_64_SHIFT_MASK)};
return ret;
}
@@ -778,22 +773,21 @@ uint64_t libdivide_u64_do(uint64_t numer, const struct libdivide_u64_t *denom) {
uint8_t more = denom->more;
if (!denom->magic) {
return numer >> more;
- }
- else {
+ } else {
uint64_t q = libdivide_mullhi_u64(denom->magic, numer);
if (more & LIBDIVIDE_ADD_MARKER) {
uint64_t t = ((numer - q) >> 1) + q;
return t >> (more & LIBDIVIDE_64_SHIFT_MASK);
- }
- else {
- // All upper bits are 0,
- // don't need to mask them off.
+ } else {
+ // All upper bits are 0,
+ // don't need to mask them off.
return q >> more;
}
}
}
-uint64_t libdivide_u64_branchfree_do(uint64_t numer, const struct libdivide_u64_branchfree_t *denom) {
+uint64_t libdivide_u64_branchfree_do(
+ uint64_t numer, const struct libdivide_u64_branchfree_t *denom) {
uint64_t q = libdivide_mullhi_u64(denom->magic, numer);
uint64_t t = ((numer - q) >> 1) + q;
return t >> denom->more;
@@ -829,13 +823,14 @@ uint64_t libdivide_u64_recover(const struct libdivide_u64_t *denom) {
// Note that the quotient is guaranteed <= 64 bits,
// but the remainder may need 65!
uint64_t r_hi, r_lo;
- uint64_t half_q = libdivide_128_div_128_to_64(half_n_hi, half_n_lo, d_hi, d_lo, &r_hi, &r_lo);
+ uint64_t half_q =
+ libdivide_128_div_128_to_64(half_n_hi, half_n_lo, d_hi, d_lo, &r_hi, &r_lo);
// We computed 2^(64+shift)/(m+2^64)
// Double the remainder ('dr') and check if that is larger than d
// Note that d is a 65 bit value, so r1 is small and so r1 + r1
// cannot overflow
uint64_t dr_lo = r_lo + r_lo;
- uint64_t dr_hi = r_hi + r_hi + (dr_lo < r_lo); // last term is carry
+ uint64_t dr_hi = r_hi + r_hi + (dr_lo < r_lo); // last term is carry
int dr_exceeds_d = (dr_hi > d_hi) || (dr_hi == d_hi && dr_lo >= d_lo);
uint64_t full_q = half_q + half_q + (dr_exceeds_d ? 1 : 0);
return full_q + 1;
@@ -863,13 +858,14 @@ uint64_t libdivide_u64_branchfree_recover(const struct libdivide_u64_branchfree_
// Note that the quotient is guaranteed <= 64 bits,
// but the remainder may need 65!
uint64_t r_hi, r_lo;
- uint64_t half_q = libdivide_128_div_128_to_64(half_n_hi, half_n_lo, d_hi, d_lo, &r_hi, &r_lo);
+ uint64_t half_q =
+ libdivide_128_div_128_to_64(half_n_hi, half_n_lo, d_hi, d_lo, &r_hi, &r_lo);
// We computed 2^(64+shift)/(m+2^64)
// Double the remainder ('dr') and check if that is larger than d
// Note that d is a 65 bit value, so r1 is small and so r1 + r1
// cannot overflow
uint64_t dr_lo = r_lo + r_lo;
- uint64_t dr_hi = r_hi + r_hi + (dr_lo < r_lo); // last term is carry
+ uint64_t dr_hi = r_hi + r_hi + (dr_lo < r_lo); // last term is carry
int dr_exceeds_d = (dr_hi > d_hi) || (dr_hi == d_hi && dr_lo >= d_lo);
uint64_t full_q = half_q + half_q + (dr_exceeds_d ? 1 : 0);
return full_q + 1;
@@ -1023,8 +1019,7 @@ int32_t libdivide_s32_recover(const struct libdivide_s32_t *denom) {
// the magic number's sign is opposite that of the divisor.
// We want to compute the positive magic number.
int negative_divisor = (more & LIBDIVIDE_NEGATIVE_DIVISOR);
- int magic_was_negated = (more & LIBDIVIDE_ADD_MARKER)
- ? denom->magic > 0 : denom->magic < 0;
+ int magic_was_negated = (more & LIBDIVIDE_ADD_MARKER) ? denom->magic > 0 : denom->magic < 0;
// Handle the power of 2 case (including branchfree)
if (denom->magic == 0) {
@@ -1033,7 +1028,7 @@ int32_t libdivide_s32_recover(const struct libdivide_s32_t *denom) {
}
uint32_t d = (uint32_t)(magic_was_negated ? -denom->magic : denom->magic);
- uint64_t n = 1ULL << (32 + shift); // this shift cannot exceed 30
+ uint64_t n = 1ULL << (32 + shift); // this shift cannot exceed 30
uint32_t q = (uint32_t)(n / d);
int32_t result = (int32_t)q;
result += 1;
@@ -1126,7 +1121,7 @@ int64_t libdivide_s64_do(int64_t numer, const struct libdivide_s64_t *denom) {
uint8_t more = denom->more;
uint8_t shift = more & LIBDIVIDE_64_SHIFT_MASK;
- if (!denom->magic) { // shift path
+ if (!denom->magic) { // shift path
uint64_t mask = (1ULL << shift) - 1;
uint64_t uq = numer + ((numer >> 63) & mask);
int64_t q = (int64_t)uq;
@@ -1178,7 +1173,7 @@ int64_t libdivide_s64_branchfree_do(int64_t numer, const struct libdivide_s64_br
int64_t libdivide_s64_recover(const struct libdivide_s64_t *denom) {
uint8_t more = denom->more;
uint8_t shift = more & LIBDIVIDE_64_SHIFT_MASK;
- if (denom->magic == 0) { // shift path
+ if (denom->magic == 0) { // shift path
uint64_t absD = 1ULL << shift;
if (more & LIBDIVIDE_NEGATIVE_DIVISOR) {
absD = -absD;
@@ -1187,8 +1182,7 @@ int64_t libdivide_s64_recover(const struct libdivide_s64_t *denom) {
} else {
// Unsigned math is much easier
int negative_divisor = (more & LIBDIVIDE_NEGATIVE_DIVISOR);
- int magic_was_negated = (more & LIBDIVIDE_ADD_MARKER)
- ? denom->magic > 0 : denom->magic < 0;
+ int magic_was_negated = (more & LIBDIVIDE_ADD_MARKER) ? denom->magic > 0 : denom->magic < 0;
uint64_t d = (uint64_t)(magic_was_negated ? -denom->magic : denom->magic);
uint64_t n_hi = 1ULL << shift, n_lo = 0;
@@ -1206,30 +1200,305 @@ int64_t libdivide_s64_branchfree_recover(const struct libdivide_s64_branchfree_t
return libdivide_s64_recover((const struct libdivide_s64_t *)denom);
}
-#if defined(LIBDIVIDE_AVX512)
+#if defined(LIBDIVIDE_NEON)
-static inline __m512i libdivide_u32_do_vector(__m512i numers, const struct libdivide_u32_t *denom);
-static inline __m512i libdivide_s32_do_vector(__m512i numers, const struct libdivide_s32_t *denom);
-static inline __m512i libdivide_u64_do_vector(__m512i numers, const struct libdivide_u64_t *denom);
-static inline __m512i libdivide_s64_do_vector(__m512i numers, const struct libdivide_s64_t *denom);
+static inline uint32x4_t libdivide_u32_do_vec128(
+ uint32x4_t numers, const struct libdivide_u32_t *denom);
+static inline int32x4_t libdivide_s32_do_vec128(
+ int32x4_t numers, const struct libdivide_s32_t *denom);
+static inline uint64x2_t libdivide_u64_do_vec128(
+ uint64x2_t numers, const struct libdivide_u64_t *denom);
+static inline int64x2_t libdivide_s64_do_vec128(
+ int64x2_t numers, const struct libdivide_s64_t *denom);
-static inline __m512i libdivide_u32_branchfree_do_vector(__m512i numers, const struct libdivide_u32_branchfree_t *denom);
-static inline __m512i libdivide_s32_branchfree_do_vector(__m512i numers, const struct libdivide_s32_branchfree_t *denom);
-static inline __m512i libdivide_u64_branchfree_do_vector(__m512i numers, const struct libdivide_u64_branchfree_t *denom);
-static inline __m512i libdivide_s64_branchfree_do_vector(__m512i numers, const struct libdivide_s64_branchfree_t *denom);
+static inline uint32x4_t libdivide_u32_branchfree_do_vec128(
+ uint32x4_t numers, const struct libdivide_u32_branchfree_t *denom);
+static inline int32x4_t libdivide_s32_branchfree_do_vec128(
+ int32x4_t numers, const struct libdivide_s32_branchfree_t *denom);
+static inline uint64x2_t libdivide_u64_branchfree_do_vec128(
+ uint64x2_t numers, const struct libdivide_u64_branchfree_t *denom);
+static inline int64x2_t libdivide_s64_branchfree_do_vec128(
+ int64x2_t numers, const struct libdivide_s64_branchfree_t *denom);
//////// Internal Utility Functions
-static inline __m512i libdivide_s64_signbits(__m512i v) {;
+// Logical right shift by runtime value.
+// NEON implements right shift as left shits by negative values.
+static inline uint32x4_t libdivide_u32_neon_srl(uint32x4_t v, uint8_t amt) {
+ int32_t wamt = static_cast(amt);
+ return vshlq_u32(v, vdupq_n_s32(-wamt));
+}
+
+static inline uint64x2_t libdivide_u64_neon_srl(uint64x2_t v, uint8_t amt) {
+ int64_t wamt = static_cast(amt);
+ return vshlq_u64(v, vdupq_n_s64(-wamt));
+}
+
+// Arithmetic right shift by runtime value.
+static inline int32x4_t libdivide_s32_neon_sra(int32x4_t v, uint8_t amt) {
+ int32_t wamt = static_cast(amt);
+ return vshlq_s32(v, vdupq_n_s32(-wamt));
+}
+
+static inline int64x2_t libdivide_s64_neon_sra(int64x2_t v, uint8_t amt) {
+ int64_t wamt = static_cast(amt);
+ return vshlq_s64(v, vdupq_n_s64(-wamt));
+}
+
+static inline int64x2_t libdivide_s64_signbits(int64x2_t v) { return vshrq_n_s64(v, 63); }
+
+static inline uint32x4_t libdivide_mullhi_u32_vec128(uint32x4_t a, uint32_t b) {
+ // Desire is [x0, x1, x2, x3]
+ uint32x4_t w1 = vreinterpretq_u32_u64(vmull_n_u32(vget_low_u32(a), b)); // [_, x0, _, x1]
+ uint32x4_t w2 = vreinterpretq_u32_u64(vmull_high_n_u32(a, b)); //[_, x2, _, x3]
+ return vuzp2q_u32(w1, w2); // [x0, x1, x2, x3]
+}
+
+static inline int32x4_t libdivide_mullhi_s32_vec128(int32x4_t a, int32_t b) {
+ int32x4_t w1 = vreinterpretq_s32_s64(vmull_n_s32(vget_low_s32(a), b)); // [_, x0, _, x1]
+ int32x4_t w2 = vreinterpretq_s32_s64(vmull_high_n_s32(a, b)); //[_, x2, _, x3]
+ return vuzp2q_s32(w1, w2); // [x0, x1, x2, x3]
+}
+
+static inline uint64x2_t libdivide_mullhi_u64_vec128(uint64x2_t x, uint64_t sy) {
+ // full 128 bits product is:
+ // x0*y0 + (x0*y1 << 32) + (x1*y0 << 32) + (x1*y1 << 64)
+ // Note x0,y0,x1,y1 are all conceptually uint32, products are 32x32->64.
+
+ // Get low and high words. x0 contains low 32 bits, x1 is high 32 bits.
+ uint64x2_t y = vdupq_n_u64(sy);
+ uint32x2_t x0 = vmovn_u64(x);
+ uint32x2_t y0 = vmovn_u64(y);
+ uint32x2_t x1 = vshrn_n_u64(x, 32);
+ uint32x2_t y1 = vshrn_n_u64(y, 32);
+
+ // Compute x0*y0.
+ uint64x2_t x0y0 = vmull_u32(x0, y0);
+ uint64x2_t x0y0_hi = vshrq_n_u64(x0y0, 32);
+
+ // Compute other intermediate products.
+ uint64x2_t temp = vmlal_u32(x0y0_hi, x1, y0); // temp = x0y0_hi + x1*y0;
+ // We want to split temp into its low 32 bits and high 32 bits, both
+ // in the low half of 64 bit registers.
+ // Use shifts to avoid needing a reg for the mask.
+ uint64x2_t temp_lo = vshrq_n_u64(vshlq_n_u64(temp, 32), 32); // temp_lo = temp & 0xFFFFFFFF;
+ uint64x2_t temp_hi = vshrq_n_u64(temp, 32); // temp_hi = temp >> 32;
+
+ temp_lo = vmlal_u32(temp_lo, x0, y1); // temp_lo += x0*y0
+ temp_lo = vshrq_n_u64(temp_lo, 32); // temp_lo >>= 32
+ temp_hi = vmlal_u32(temp_hi, x1, y1); // temp_hi += x1*y1
+ uint64x2_t result = vaddq_u64(temp_hi, temp_lo);
+ return result;
+}
+
+static inline int64x2_t libdivide_mullhi_s64_vec128(int64x2_t x, int64_t sy) {
+ int64x2_t p = vreinterpretq_s64_u64(
+ libdivide_mullhi_u64_vec128(vreinterpretq_u64_s64(x), static_cast(sy)));
+ int64x2_t y = vdupq_n_s64(sy);
+ int64x2_t t1 = vandq_s64(libdivide_s64_signbits(x), y);
+ int64x2_t t2 = vandq_s64(libdivide_s64_signbits(y), x);
+ p = vsubq_s64(p, t1);
+ p = vsubq_s64(p, t2);
+ return p;
+}
+
+////////// UINT32
+
+uint32x4_t libdivide_u32_do_vec128(uint32x4_t numers, const struct libdivide_u32_t *denom) {
+ uint8_t more = denom->more;
+ if (!denom->magic) {
+ return libdivide_u32_neon_srl(numers, more);
+ } else {
+ uint32x4_t q = libdivide_mullhi_u32_vec128(numers, denom->magic);
+ if (more & LIBDIVIDE_ADD_MARKER) {
+ // uint32_t t = ((numer - q) >> 1) + q;
+ // return t >> denom->shift;
+ // Note we can use halving-subtract to avoid the shift.
+ uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK;
+ uint32x4_t t = vaddq_u32(vhsubq_u32(numers, q), q);
+ return libdivide_u32_neon_srl(t, shift);
+ } else {
+ return libdivide_u32_neon_srl(q, more);
+ }
+ }
+}
+
+uint32x4_t libdivide_u32_branchfree_do_vec128(
+ uint32x4_t numers, const struct libdivide_u32_branchfree_t *denom) {
+ uint32x4_t q = libdivide_mullhi_u32_vec128(numers, denom->magic);
+ uint32x4_t t = vaddq_u32(vhsubq_u32(numers, q), q);
+ return libdivide_u32_neon_srl(t, denom->more);
+}
+
+////////// UINT64
+
+uint64x2_t libdivide_u64_do_vec128(uint64x2_t numers, const struct libdivide_u64_t *denom) {
+ uint8_t more = denom->more;
+ if (!denom->magic) {
+ return libdivide_u64_neon_srl(numers, more);
+ } else {
+ uint64x2_t q = libdivide_mullhi_u64_vec128(numers, denom->magic);
+ if (more & LIBDIVIDE_ADD_MARKER) {
+ // uint32_t t = ((numer - q) >> 1) + q;
+ // return t >> denom->shift;
+ // No 64-bit halving subtracts in NEON :(
+ uint8_t shift = more & LIBDIVIDE_64_SHIFT_MASK;
+ uint64x2_t t = vaddq_u64(vshrq_n_u64(vsubq_u64(numers, q), 1), q);
+ return libdivide_u64_neon_srl(t, shift);
+ } else {
+ return libdivide_u64_neon_srl(q, more);
+ }
+ }
+}
+
+uint64x2_t libdivide_u64_branchfree_do_vec128(
+ uint64x2_t numers, const struct libdivide_u64_branchfree_t *denom) {
+ uint64x2_t q = libdivide_mullhi_u64_vec128(numers, denom->magic);
+ uint64x2_t t = vaddq_u64(vshrq_n_u64(vsubq_u64(numers, q), 1), q);
+ return libdivide_u64_neon_srl(t, denom->more);
+}
+
+////////// SINT32
+
+int32x4_t libdivide_s32_do_vec128(int32x4_t numers, const struct libdivide_s32_t *denom) {
+ uint8_t more = denom->more;
+ if (!denom->magic) {
+ uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK;
+ uint32_t mask = (1U << shift) - 1;
+ int32x4_t roundToZeroTweak = vdupq_n_s32((int)mask);
+ // q = numer + ((numer >> 31) & roundToZeroTweak);
+ int32x4_t q = vaddq_s32(numers, vandq_s32(vshrq_n_s32(numers, 31), roundToZeroTweak));
+ q = libdivide_s32_neon_sra(q, shift);
+ int32x4_t sign = vdupq_n_s32((int8_t)more >> 7);
+ // q = (q ^ sign) - sign;
+ q = vsubq_s32(veorq_s32(q, sign), sign);
+ return q;
+ } else {
+ int32x4_t q = libdivide_mullhi_s32_vec128(numers, denom->magic);
+ if (more & LIBDIVIDE_ADD_MARKER) {
+ // must be arithmetic shift
+ int32x4_t sign = vdupq_n_s32((int8_t)more >> 7);
+ // q += ((numer ^ sign) - sign);
+ q = vaddq_s32(q, vsubq_s32(veorq_s32(numers, sign), sign));
+ }
+ // q >>= shift
+ q = libdivide_s32_neon_sra(q, more & LIBDIVIDE_32_SHIFT_MASK);
+ q = vaddq_s32(
+ q, vreinterpretq_s32_u32(vshrq_n_u32(vreinterpretq_u32_s32(q), 31))); // q += (q < 0)
+ return q;
+ }
+}
+
+int32x4_t libdivide_s32_branchfree_do_vec128(
+ int32x4_t numers, const struct libdivide_s32_branchfree_t *denom) {
+ int32_t magic = denom->magic;
+ uint8_t more = denom->more;
+ uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK;
+ // must be arithmetic shift
+ int32x4_t sign = vdupq_n_s32((int8_t)more >> 7);
+ int32x4_t q = libdivide_mullhi_s32_vec128(numers, magic);
+ q = vaddq_s32(q, numers); // q += numers
+
+ // If q is non-negative, we have nothing to do
+ // If q is negative, we want to add either (2**shift)-1 if d is
+ // a power of 2, or (2**shift) if it is not a power of 2
+ uint32_t is_power_of_2 = (magic == 0);
+ int32x4_t q_sign = vshrq_n_s32(q, 31); // q_sign = q >> 31
+ int32x4_t mask = vdupq_n_s32((1U << shift) - is_power_of_2);
+ q = vaddq_s32(q, vandq_s32(q_sign, mask)); // q = q + (q_sign & mask)
+ q = libdivide_s32_neon_sra(q, shift); // q >>= shift
+ q = vsubq_s32(veorq_s32(q, sign), sign); // q = (q ^ sign) - sign
+ return q;
+}
+
+////////// SINT64
+
+int64x2_t libdivide_s64_do_vec128(int64x2_t numers, const struct libdivide_s64_t *denom) {
+ uint8_t more = denom->more;
+ int64_t magic = denom->magic;
+ if (magic == 0) { // shift path
+ uint8_t shift = more & LIBDIVIDE_64_SHIFT_MASK;
+ uint64_t mask = (1ULL << shift) - 1;
+ int64x2_t roundToZeroTweak = vdupq_n_s64(mask); // TODO: no need to sign extend
+ // q = numer + ((numer >> 63) & roundToZeroTweak);
+ int64x2_t q =
+ vaddq_s64(numers, vandq_s64(libdivide_s64_signbits(numers), roundToZeroTweak));
+ q = libdivide_s64_neon_sra(q, shift);
+ // q = (q ^ sign) - sign;
+ int64x2_t sign = vreinterpretq_s64_s8(vdupq_n_s8((int8_t)more >> 7));
+ q = vsubq_s64(veorq_s64(q, sign), sign);
+ return q;
+ } else {
+ int64x2_t q = libdivide_mullhi_s64_vec128(numers, magic);
+ if (more & LIBDIVIDE_ADD_MARKER) {
+ // must be arithmetic shift
+ int64x2_t sign = vdupq_n_s64((int8_t)more >> 7); // TODO: no need to widen
+ // q += ((numer ^ sign) - sign);
+ q = vaddq_s64(q, vsubq_s64(veorq_s64(numers, sign), sign));
+ }
+ // q >>= denom->mult_path.shift
+ q = libdivide_s64_neon_sra(q, more & LIBDIVIDE_64_SHIFT_MASK);
+ q = vaddq_s64(
+ q, vreinterpretq_s64_u64(vshrq_n_u64(vreinterpretq_u64_s64(q), 63))); // q += (q < 0)
+ return q;
+ }
+}
+
+int64x2_t libdivide_s64_branchfree_do_vec128(
+ int64x2_t numers, const struct libdivide_s64_branchfree_t *denom) {
+ int64_t magic = denom->magic;
+ uint8_t more = denom->more;
+ uint8_t shift = more & LIBDIVIDE_64_SHIFT_MASK;
+ // must be arithmetic shift
+ int64x2_t sign = vdupq_n_s64((int8_t)more >> 7); // TODO: avoid sign extend
+
+ // libdivide_mullhi_s64(numers, magic);
+ int64x2_t q = libdivide_mullhi_s64_vec128(numers, magic);
+ q = vaddq_s64(q, numers); // q += numers
+
+ // If q is non-negative, we have nothing to do.
+ // If q is negative, we want to add either (2**shift)-1 if d is
+ // a power of 2, or (2**shift) if it is not a power of 2.
+ uint32_t is_power_of_2 = (magic == 0);
+ int64x2_t q_sign = libdivide_s64_signbits(q); // q_sign = q >> 63
+ int64x2_t mask = vdupq_n_s64((1ULL << shift) - is_power_of_2);
+ q = vaddq_s64(q, vandq_s64(q_sign, mask)); // q = q + (q_sign & mask)
+ q = libdivide_s64_neon_sra(q, shift); // q >>= shift
+ q = vsubq_s64(veorq_s64(q, sign), sign); // q = (q ^ sign) - sign
+ return q;
+}
+
+#endif
+
+#if defined(LIBDIVIDE_AVX512)
+
+static inline __m512i libdivide_u32_do_vec512(__m512i numers, const struct libdivide_u32_t *denom);
+static inline __m512i libdivide_s32_do_vec512(__m512i numers, const struct libdivide_s32_t *denom);
+static inline __m512i libdivide_u64_do_vec512(__m512i numers, const struct libdivide_u64_t *denom);
+static inline __m512i libdivide_s64_do_vec512(__m512i numers, const struct libdivide_s64_t *denom);
+
+static inline __m512i libdivide_u32_branchfree_do_vec512(
+ __m512i numers, const struct libdivide_u32_branchfree_t *denom);
+static inline __m512i libdivide_s32_branchfree_do_vec512(
+ __m512i numers, const struct libdivide_s32_branchfree_t *denom);
+static inline __m512i libdivide_u64_branchfree_do_vec512(
+ __m512i numers, const struct libdivide_u64_branchfree_t *denom);
+static inline __m512i libdivide_s64_branchfree_do_vec512(
+ __m512i numers, const struct libdivide_s64_branchfree_t *denom);
+
+//////// Internal Utility Functions
+
+static inline __m512i libdivide_s64_signbits(__m512i v) {
+ ;
return _mm512_srai_epi64(v, 63);
}
-static inline __m512i libdivide_s64_shift_right_vector(__m512i v, int amt) {
+static inline __m512i libdivide_s64_shift_right_vec512(__m512i v, int amt) {
return _mm512_srai_epi64(v, amt);
}
// Here, b is assumed to contain one 32-bit value repeated.
-static inline __m512i libdivide_mullhi_u32_vector(__m512i a, __m512i b) {
+static inline __m512i libdivide_mullhi_u32_vec512(__m512i a, __m512i b) {
__m512i hi_product_0Z2Z = _mm512_srli_epi64(_mm512_mul_epu32(a, b), 32);
__m512i a1X3X = _mm512_srli_epi64(a, 32);
__m512i mask = _mm512_set_epi32(-1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0);
@@ -1238,7 +1507,7 @@ static inline __m512i libdivide_mullhi_u32_vector(__m512i a, __m512i b) {
}
// b is one 32-bit value repeated.
-static inline __m512i libdivide_mullhi_s32_vector(__m512i a, __m512i b) {
+static inline __m512i libdivide_mullhi_s32_vec512(__m512i a, __m512i b) {
__m512i hi_product_0Z2Z = _mm512_srli_epi64(_mm512_mul_epi32(a, b), 32);
__m512i a1X3X = _mm512_srli_epi64(a, 32);
__m512i mask = _mm512_set_epi32(-1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0);
@@ -1247,30 +1516,31 @@ static inline __m512i libdivide_mullhi_s32_vector(__m512i a, __m512i b) {
}
// Here, y is assumed to contain one 64-bit value repeated.
-// https://stackoverflow.com/a/28827013
-static inline __m512i libdivide_mullhi_u64_vector(__m512i x, __m512i y) {
- __m512i lomask = _mm512_set1_epi64(0xffffffff);
- __m512i xh = _mm512_shuffle_epi32(x, (_MM_PERM_ENUM) 0xB1);
- __m512i yh = _mm512_shuffle_epi32(y, (_MM_PERM_ENUM) 0xB1);
- __m512i w0 = _mm512_mul_epu32(x, y);
- __m512i w1 = _mm512_mul_epu32(x, yh);
- __m512i w2 = _mm512_mul_epu32(xh, y);
- __m512i w3 = _mm512_mul_epu32(xh, yh);
- __m512i w0h = _mm512_srli_epi64(w0, 32);
- __m512i s1 = _mm512_add_epi64(w1, w0h);
- __m512i s1l = _mm512_and_si512(s1, lomask);
- __m512i s1h = _mm512_srli_epi64(s1, 32);
- __m512i s2 = _mm512_add_epi64(w2, s1l);
- __m512i s2h = _mm512_srli_epi64(s2, 32);
- __m512i hi = _mm512_add_epi64(w3, s1h);
- hi = _mm512_add_epi64(hi, s2h);
+static inline __m512i libdivide_mullhi_u64_vec512(__m512i x, __m512i y) {
+ // see m128i variant for comments.
+ __m512i x0y0 = _mm512_mul_epu32(x, y);
+ __m512i x0y0_hi = _mm512_srli_epi64(x0y0, 32);
- return hi;
+ __m512i x1 = _mm512_shuffle_epi32(x, (_MM_PERM_ENUM)_MM_SHUFFLE(3, 3, 1, 1));
+ __m512i y1 = _mm512_shuffle_epi32(y, (_MM_PERM_ENUM)_MM_SHUFFLE(3, 3, 1, 1));
+
+ __m512i x0y1 = _mm512_mul_epu32(x, y1);
+ __m512i x1y0 = _mm512_mul_epu32(x1, y);
+ __m512i x1y1 = _mm512_mul_epu32(x1, y1);
+
+ __m512i mask = _mm512_set1_epi64(0xFFFFFFFF);
+ __m512i temp = _mm512_add_epi64(x1y0, x0y0_hi);
+ __m512i temp_lo = _mm512_and_si512(temp, mask);
+ __m512i temp_hi = _mm512_srli_epi64(temp, 32);
+
+ temp_lo = _mm512_srli_epi64(_mm512_add_epi64(temp_lo, x0y1), 32);
+ temp_hi = _mm512_add_epi64(x1y1, temp_hi);
+ return _mm512_add_epi64(temp_lo, temp_hi);
}
// y is one 64-bit value repeated.
-static inline __m512i libdivide_mullhi_s64_vector(__m512i x, __m512i y) {
- __m512i p = libdivide_mullhi_u64_vector(x, y);
+static inline __m512i libdivide_mullhi_s64_vec512(__m512i x, __m512i y) {
+ __m512i p = libdivide_mullhi_u64_vec512(x, y);
__m512i t1 = _mm512_and_si512(libdivide_s64_signbits(x), y);
__m512i t2 = _mm512_and_si512(libdivide_s64_signbits(y), x);
p = _mm512_sub_epi64(p, t1);
@@ -1280,131 +1550,130 @@ static inline __m512i libdivide_mullhi_s64_vector(__m512i x, __m512i y) {
////////// UINT32
-__m512i libdivide_u32_do_vector(__m512i numers, const struct libdivide_u32_t *denom) {
+__m512i libdivide_u32_do_vec512(__m512i numers, const struct libdivide_u32_t *denom) {
uint8_t more = denom->more;
if (!denom->magic) {
return _mm512_srli_epi32(numers, more);
- }
- else {
- __m512i q = libdivide_mullhi_u32_vector(numers, _mm512_set1_epi32(denom->magic));
+ } else {
+ __m512i q = libdivide_mullhi_u32_vec512(numers, _mm512_set1_epi32(denom->magic));
if (more & LIBDIVIDE_ADD_MARKER) {
// uint32_t t = ((numer - q) >> 1) + q;
// return t >> denom->shift;
uint32_t shift = more & LIBDIVIDE_32_SHIFT_MASK;
__m512i t = _mm512_add_epi32(_mm512_srli_epi32(_mm512_sub_epi32(numers, q), 1), q);
return _mm512_srli_epi32(t, shift);
- }
- else {
+ } else {
return _mm512_srli_epi32(q, more);
}
}
}
-__m512i libdivide_u32_branchfree_do_vector(__m512i numers, const struct libdivide_u32_branchfree_t *denom) {
- __m512i q = libdivide_mullhi_u32_vector(numers, _mm512_set1_epi32(denom->magic));
+__m512i libdivide_u32_branchfree_do_vec512(
+ __m512i numers, const struct libdivide_u32_branchfree_t *denom) {
+ __m512i q = libdivide_mullhi_u32_vec512(numers, _mm512_set1_epi32(denom->magic));
__m512i t = _mm512_add_epi32(_mm512_srli_epi32(_mm512_sub_epi32(numers, q), 1), q);
return _mm512_srli_epi32(t, denom->more);
}
////////// UINT64
-__m512i libdivide_u64_do_vector(__m512i numers, const struct libdivide_u64_t *denom) {
+__m512i libdivide_u64_do_vec512(__m512i numers, const struct libdivide_u64_t *denom) {
uint8_t more = denom->more;
if (!denom->magic) {
return _mm512_srli_epi64(numers, more);
- }
- else {
- __m512i q = libdivide_mullhi_u64_vector(numers, _mm512_set1_epi64(denom->magic));
+ } else {
+ __m512i q = libdivide_mullhi_u64_vec512(numers, _mm512_set1_epi64(denom->magic));
if (more & LIBDIVIDE_ADD_MARKER) {
// uint32_t t = ((numer - q) >> 1) + q;
// return t >> denom->shift;
uint32_t shift = more & LIBDIVIDE_64_SHIFT_MASK;
__m512i t = _mm512_add_epi64(_mm512_srli_epi64(_mm512_sub_epi64(numers, q), 1), q);
return _mm512_srli_epi64(t, shift);
- }
- else {
+ } else {
return _mm512_srli_epi64(q, more);
}
}
}
-__m512i libdivide_u64_branchfree_do_vector(__m512i numers, const struct libdivide_u64_branchfree_t *denom) {
- __m512i q = libdivide_mullhi_u64_vector(numers, _mm512_set1_epi64(denom->magic));
+__m512i libdivide_u64_branchfree_do_vec512(
+ __m512i numers, const struct libdivide_u64_branchfree_t *denom) {
+ __m512i q = libdivide_mullhi_u64_vec512(numers, _mm512_set1_epi64(denom->magic));
__m512i t = _mm512_add_epi64(_mm512_srli_epi64(_mm512_sub_epi64(numers, q), 1), q);
return _mm512_srli_epi64(t, denom->more);
}
////////// SINT32
-__m512i libdivide_s32_do_vector(__m512i numers, const struct libdivide_s32_t *denom) {
+__m512i libdivide_s32_do_vec512(__m512i numers, const struct libdivide_s32_t *denom) {
uint8_t more = denom->more;
if (!denom->magic) {
uint32_t shift = more & LIBDIVIDE_32_SHIFT_MASK;
uint32_t mask = (1U << shift) - 1;
__m512i roundToZeroTweak = _mm512_set1_epi32(mask);
// q = numer + ((numer >> 31) & roundToZeroTweak);
- __m512i q = _mm512_add_epi32(numers, _mm512_and_si512(_mm512_srai_epi32(numers, 31), roundToZeroTweak));
+ __m512i q = _mm512_add_epi32(
+ numers, _mm512_and_si512(_mm512_srai_epi32(numers, 31), roundToZeroTweak));
q = _mm512_srai_epi32(q, shift);
__m512i sign = _mm512_set1_epi32((int8_t)more >> 7);
// q = (q ^ sign) - sign;
q = _mm512_sub_epi32(_mm512_xor_si512(q, sign), sign);
return q;
- }
- else {
- __m512i q = libdivide_mullhi_s32_vector(numers, _mm512_set1_epi32(denom->magic));
+ } else {
+ __m512i q = libdivide_mullhi_s32_vec512(numers, _mm512_set1_epi32(denom->magic));
if (more & LIBDIVIDE_ADD_MARKER) {
- // must be arithmetic shift
+ // must be arithmetic shift
__m512i sign = _mm512_set1_epi32((int8_t)more >> 7);
- // q += ((numer ^ sign) - sign);
+ // q += ((numer ^ sign) - sign);
q = _mm512_add_epi32(q, _mm512_sub_epi32(_mm512_xor_si512(numers, sign), sign));
}
// q >>= shift
q = _mm512_srai_epi32(q, more & LIBDIVIDE_32_SHIFT_MASK);
- q = _mm512_add_epi32(q, _mm512_srli_epi32(q, 31)); // q += (q < 0)
+ q = _mm512_add_epi32(q, _mm512_srli_epi32(q, 31)); // q += (q < 0)
return q;
}
}
-__m512i libdivide_s32_branchfree_do_vector(__m512i numers, const struct libdivide_s32_branchfree_t *denom) {
+__m512i libdivide_s32_branchfree_do_vec512(
+ __m512i numers, const struct libdivide_s32_branchfree_t *denom) {
int32_t magic = denom->magic;
uint8_t more = denom->more;
uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK;
- // must be arithmetic shift
+ // must be arithmetic shift
__m512i sign = _mm512_set1_epi32((int8_t)more >> 7);
- __m512i q = libdivide_mullhi_s32_vector(numers, _mm512_set1_epi32(magic));
- q = _mm512_add_epi32(q, numers); // q += numers
+ __m512i q = libdivide_mullhi_s32_vec512(numers, _mm512_set1_epi32(magic));
+ q = _mm512_add_epi32(q, numers); // q += numers
// If q is non-negative, we have nothing to do
// If q is negative, we want to add either (2**shift)-1 if d is
// a power of 2, or (2**shift) if it is not a power of 2
uint32_t is_power_of_2 = (magic == 0);
- __m512i q_sign = _mm512_srai_epi32(q, 31); // q_sign = q >> 31
+ __m512i q_sign = _mm512_srai_epi32(q, 31); // q_sign = q >> 31
__m512i mask = _mm512_set1_epi32((1U << shift) - is_power_of_2);
- q = _mm512_add_epi32(q, _mm512_and_si512(q_sign, mask)); // q = q + (q_sign & mask)
- q = _mm512_srai_epi32(q, shift); // q >>= shift
- q = _mm512_sub_epi32(_mm512_xor_si512(q, sign), sign); // q = (q ^ sign) - sign
+ q = _mm512_add_epi32(q, _mm512_and_si512(q_sign, mask)); // q = q + (q_sign & mask)
+ q = _mm512_srai_epi32(q, shift); // q >>= shift
+ q = _mm512_sub_epi32(_mm512_xor_si512(q, sign), sign); // q = (q ^ sign) - sign
return q;
}
////////// SINT64
-__m512i libdivide_s64_do_vector(__m512i numers, const struct libdivide_s64_t *denom) {
+__m512i libdivide_s64_do_vec512(__m512i numers, const struct libdivide_s64_t *denom) {
uint8_t more = denom->more;
int64_t magic = denom->magic;
- if (magic == 0) { // shift path
+ if (magic == 0) { // shift path
uint32_t shift = more & LIBDIVIDE_64_SHIFT_MASK;
uint64_t mask = (1ULL << shift) - 1;
__m512i roundToZeroTweak = _mm512_set1_epi64(mask);
// q = numer + ((numer >> 63) & roundToZeroTweak);
- __m512i q = _mm512_add_epi64(numers, _mm512_and_si512(libdivide_s64_signbits(numers), roundToZeroTweak));
- q = libdivide_s64_shift_right_vector(q, shift);
+ __m512i q = _mm512_add_epi64(
+ numers, _mm512_and_si512(libdivide_s64_signbits(numers), roundToZeroTweak));
+ q = libdivide_s64_shift_right_vec512(q, shift);
__m512i sign = _mm512_set1_epi32((int8_t)more >> 7);
- // q = (q ^ sign) - sign;
+ // q = (q ^ sign) - sign;
q = _mm512_sub_epi64(_mm512_xor_si512(q, sign), sign);
return q;
- }
- else {
- __m512i q = libdivide_mullhi_s64_vector(numers, _mm512_set1_epi64(magic));
+ } else {
+ __m512i q = libdivide_mullhi_s64_vec512(numers, _mm512_set1_epi64(magic));
if (more & LIBDIVIDE_ADD_MARKER) {
// must be arithmetic shift
__m512i sign = _mm512_set1_epi32((int8_t)more >> 7);
@@ -1412,46 +1681,53 @@ __m512i libdivide_s64_do_vector(__m512i numers, const struct libdivide_s64_t *de
q = _mm512_add_epi64(q, _mm512_sub_epi64(_mm512_xor_si512(numers, sign), sign));
}
// q >>= denom->mult_path.shift
- q = libdivide_s64_shift_right_vector(q, more & LIBDIVIDE_64_SHIFT_MASK);
- q = _mm512_add_epi64(q, _mm512_srli_epi64(q, 63)); // q += (q < 0)
+ q = libdivide_s64_shift_right_vec512(q, more & LIBDIVIDE_64_SHIFT_MASK);
+ q = _mm512_add_epi64(q, _mm512_srli_epi64(q, 63)); // q += (q < 0)
return q;
}
}
-__m512i libdivide_s64_branchfree_do_vector(__m512i numers, const struct libdivide_s64_branchfree_t *denom) {
+__m512i libdivide_s64_branchfree_do_vec512(
+ __m512i numers, const struct libdivide_s64_branchfree_t *denom) {
int64_t magic = denom->magic;
uint8_t more = denom->more;
uint8_t shift = more & LIBDIVIDE_64_SHIFT_MASK;
// must be arithmetic shift
__m512i sign = _mm512_set1_epi32((int8_t)more >> 7);
- // libdivide_mullhi_s64(numers, magic);
- __m512i q = libdivide_mullhi_s64_vector(numers, _mm512_set1_epi64(magic));
- q = _mm512_add_epi64(q, numers); // q += numers
+ // libdivide_mullhi_s64(numers, magic);
+ __m512i q = libdivide_mullhi_s64_vec512(numers, _mm512_set1_epi64(magic));
+ q = _mm512_add_epi64(q, numers); // q += numers
// If q is non-negative, we have nothing to do.
// If q is negative, we want to add either (2**shift)-1 if d is
// a power of 2, or (2**shift) if it is not a power of 2.
uint32_t is_power_of_2 = (magic == 0);
- __m512i q_sign = libdivide_s64_signbits(q); // q_sign = q >> 63
+ __m512i q_sign = libdivide_s64_signbits(q); // q_sign = q >> 63
__m512i mask = _mm512_set1_epi64((1ULL << shift) - is_power_of_2);
- q = _mm512_add_epi64(q, _mm512_and_si512(q_sign, mask)); // q = q + (q_sign & mask)
- q = libdivide_s64_shift_right_vector(q, shift); // q >>= shift
- q = _mm512_sub_epi64(_mm512_xor_si512(q, sign), sign); // q = (q ^ sign) - sign
+ q = _mm512_add_epi64(q, _mm512_and_si512(q_sign, mask)); // q = q + (q_sign & mask)
+ q = libdivide_s64_shift_right_vec512(q, shift); // q >>= shift
+ q = _mm512_sub_epi64(_mm512_xor_si512(q, sign), sign); // q = (q ^ sign) - sign
return q;
}
-#elif defined(LIBDIVIDE_AVX2)
+#endif
-static inline __m256i libdivide_u32_do_vector(__m256i numers, const struct libdivide_u32_t *denom);
-static inline __m256i libdivide_s32_do_vector(__m256i numers, const struct libdivide_s32_t *denom);
-static inline __m256i libdivide_u64_do_vector(__m256i numers, const struct libdivide_u64_t *denom);
-static inline __m256i libdivide_s64_do_vector(__m256i numers, const struct libdivide_s64_t *denom);
+#if defined(LIBDIVIDE_AVX2)
-static inline __m256i libdivide_u32_branchfree_do_vector(__m256i numers, const struct libdivide_u32_branchfree_t *denom);
-static inline __m256i libdivide_s32_branchfree_do_vector(__m256i numers, const struct libdivide_s32_branchfree_t *denom);
-static inline __m256i libdivide_u64_branchfree_do_vector(__m256i numers, const struct libdivide_u64_branchfree_t *denom);
-static inline __m256i libdivide_s64_branchfree_do_vector(__m256i numers, const struct libdivide_s64_branchfree_t *denom);
+static inline __m256i libdivide_u32_do_vec256(__m256i numers, const struct libdivide_u32_t *denom);
+static inline __m256i libdivide_s32_do_vec256(__m256i numers, const struct libdivide_s32_t *denom);
+static inline __m256i libdivide_u64_do_vec256(__m256i numers, const struct libdivide_u64_t *denom);
+static inline __m256i libdivide_s64_do_vec256(__m256i numers, const struct libdivide_s64_t *denom);
+
+static inline __m256i libdivide_u32_branchfree_do_vec256(
+ __m256i numers, const struct libdivide_u32_branchfree_t *denom);
+static inline __m256i libdivide_s32_branchfree_do_vec256(
+ __m256i numers, const struct libdivide_s32_branchfree_t *denom);
+static inline __m256i libdivide_u64_branchfree_do_vec256(
+ __m256i numers, const struct libdivide_u64_branchfree_t *denom);
+static inline __m256i libdivide_s64_branchfree_do_vec256(
+ __m256i numers, const struct libdivide_s64_branchfree_t *denom);
//////// Internal Utility Functions
@@ -1463,7 +1739,7 @@ static inline __m256i libdivide_s64_signbits(__m256i v) {
}
// Implementation of _mm256_srai_epi64 (from AVX512).
-static inline __m256i libdivide_s64_shift_right_vector(__m256i v, int amt) {
+static inline __m256i libdivide_s64_shift_right_vec256(__m256i v, int amt) {
const int b = 64 - amt;
__m256i m = _mm256_set1_epi64x(1ULL << (b - 1));
__m256i x = _mm256_srli_epi64(v, amt);
@@ -1472,7 +1748,7 @@ static inline __m256i libdivide_s64_shift_right_vector(__m256i v, int amt) {
}
// Here, b is assumed to contain one 32-bit value repeated.
-static inline __m256i libdivide_mullhi_u32_vector(__m256i a, __m256i b) {
+static inline __m256i libdivide_mullhi_u32_vec256(__m256i a, __m256i b) {
__m256i hi_product_0Z2Z = _mm256_srli_epi64(_mm256_mul_epu32(a, b), 32);
__m256i a1X3X = _mm256_srli_epi64(a, 32);
__m256i mask = _mm256_set_epi32(-1, 0, -1, 0, -1, 0, -1, 0);
@@ -1481,7 +1757,7 @@ static inline __m256i libdivide_mullhi_u32_vector(__m256i a, __m256i b) {
}
// b is one 32-bit value repeated.
-static inline __m256i libdivide_mullhi_s32_vector(__m256i a, __m256i b) {
+static inline __m256i libdivide_mullhi_s32_vec256(__m256i a, __m256i b) {
__m256i hi_product_0Z2Z = _mm256_srli_epi64(_mm256_mul_epi32(a, b), 32);
__m256i a1X3X = _mm256_srli_epi64(a, 32);
__m256i mask = _mm256_set_epi32(-1, 0, -1, 0, -1, 0, -1, 0);
@@ -1490,30 +1766,31 @@ static inline __m256i libdivide_mullhi_s32_vector(__m256i a, __m256i b) {
}
// Here, y is assumed to contain one 64-bit value repeated.
-// https://stackoverflow.com/a/28827013
-static inline __m256i libdivide_mullhi_u64_vector(__m256i x, __m256i y) {
- __m256i lomask = _mm256_set1_epi64x(0xffffffff);
- __m256i xh = _mm256_shuffle_epi32(x, 0xB1); // x0l, x0h, x1l, x1h
- __m256i yh = _mm256_shuffle_epi32(y, 0xB1); // y0l, y0h, y1l, y1h
- __m256i w0 = _mm256_mul_epu32(x, y); // x0l*y0l, x1l*y1l
- __m256i w1 = _mm256_mul_epu32(x, yh); // x0l*y0h, x1l*y1h
- __m256i w2 = _mm256_mul_epu32(xh, y); // x0h*y0l, x1h*y0l
- __m256i w3 = _mm256_mul_epu32(xh, yh); // x0h*y0h, x1h*y1h
- __m256i w0h = _mm256_srli_epi64(w0, 32);
- __m256i s1 = _mm256_add_epi64(w1, w0h);
- __m256i s1l = _mm256_and_si256(s1, lomask);
- __m256i s1h = _mm256_srli_epi64(s1, 32);
- __m256i s2 = _mm256_add_epi64(w2, s1l);
- __m256i s2h = _mm256_srli_epi64(s2, 32);
- __m256i hi = _mm256_add_epi64(w3, s1h);
- hi = _mm256_add_epi64(hi, s2h);
+static inline __m256i libdivide_mullhi_u64_vec256(__m256i x, __m256i y) {
+ // see m128i variant for comments.
+ __m256i x0y0 = _mm256_mul_epu32(x, y);
+ __m256i x0y0_hi = _mm256_srli_epi64(x0y0, 32);
- return hi;
+ __m256i x1 = _mm256_shuffle_epi32(x, _MM_SHUFFLE(3, 3, 1, 1));
+ __m256i y1 = _mm256_shuffle_epi32(y, _MM_SHUFFLE(3, 3, 1, 1));
+
+ __m256i x0y1 = _mm256_mul_epu32(x, y1);
+ __m256i x1y0 = _mm256_mul_epu32(x1, y);
+ __m256i x1y1 = _mm256_mul_epu32(x1, y1);
+
+ __m256i mask = _mm256_set1_epi64x(0xFFFFFFFF);
+ __m256i temp = _mm256_add_epi64(x1y0, x0y0_hi);
+ __m256i temp_lo = _mm256_and_si256(temp, mask);
+ __m256i temp_hi = _mm256_srli_epi64(temp, 32);
+
+ temp_lo = _mm256_srli_epi64(_mm256_add_epi64(temp_lo, x0y1), 32);
+ temp_hi = _mm256_add_epi64(x1y1, temp_hi);
+ return _mm256_add_epi64(temp_lo, temp_hi);
}
// y is one 64-bit value repeated.
-static inline __m256i libdivide_mullhi_s64_vector(__m256i x, __m256i y) {
- __m256i p = libdivide_mullhi_u64_vector(x, y);
+static inline __m256i libdivide_mullhi_s64_vec256(__m256i x, __m256i y) {
+ __m256i p = libdivide_mullhi_u64_vec256(x, y);
__m256i t1 = _mm256_and_si256(libdivide_s64_signbits(x), y);
__m256i t2 = _mm256_and_si256(libdivide_s64_signbits(y), x);
p = _mm256_sub_epi64(p, t1);
@@ -1523,131 +1800,130 @@ static inline __m256i libdivide_mullhi_s64_vector(__m256i x, __m256i y) {
////////// UINT32
-__m256i libdivide_u32_do_vector(__m256i numers, const struct libdivide_u32_t *denom) {
+__m256i libdivide_u32_do_vec256(__m256i numers, const struct libdivide_u32_t *denom) {
uint8_t more = denom->more;
if (!denom->magic) {
return _mm256_srli_epi32(numers, more);
- }
- else {
- __m256i q = libdivide_mullhi_u32_vector(numers, _mm256_set1_epi32(denom->magic));
+ } else {
+ __m256i q = libdivide_mullhi_u32_vec256(numers, _mm256_set1_epi32(denom->magic));
if (more & LIBDIVIDE_ADD_MARKER) {
// uint32_t t = ((numer - q) >> 1) + q;
// return t >> denom->shift;
uint32_t shift = more & LIBDIVIDE_32_SHIFT_MASK;
__m256i t = _mm256_add_epi32(_mm256_srli_epi32(_mm256_sub_epi32(numers, q), 1), q);
return _mm256_srli_epi32(t, shift);
- }
- else {
+ } else {
return _mm256_srli_epi32(q, more);
}
}
}
-__m256i libdivide_u32_branchfree_do_vector(__m256i numers, const struct libdivide_u32_branchfree_t *denom) {
- __m256i q = libdivide_mullhi_u32_vector(numers, _mm256_set1_epi32(denom->magic));
+__m256i libdivide_u32_branchfree_do_vec256(
+ __m256i numers, const struct libdivide_u32_branchfree_t *denom) {
+ __m256i q = libdivide_mullhi_u32_vec256(numers, _mm256_set1_epi32(denom->magic));
__m256i t = _mm256_add_epi32(_mm256_srli_epi32(_mm256_sub_epi32(numers, q), 1), q);
return _mm256_srli_epi32(t, denom->more);
}
////////// UINT64
-__m256i libdivide_u64_do_vector(__m256i numers, const struct libdivide_u64_t *denom) {
+__m256i libdivide_u64_do_vec256(__m256i numers, const struct libdivide_u64_t *denom) {
uint8_t more = denom->more;
if (!denom->magic) {
return _mm256_srli_epi64(numers, more);
- }
- else {
- __m256i q = libdivide_mullhi_u64_vector(numers, _mm256_set1_epi64x(denom->magic));
+ } else {
+ __m256i q = libdivide_mullhi_u64_vec256(numers, _mm256_set1_epi64x(denom->magic));
if (more & LIBDIVIDE_ADD_MARKER) {
// uint32_t t = ((numer - q) >> 1) + q;
// return t >> denom->shift;
uint32_t shift = more & LIBDIVIDE_64_SHIFT_MASK;
__m256i t = _mm256_add_epi64(_mm256_srli_epi64(_mm256_sub_epi64(numers, q), 1), q);
return _mm256_srli_epi64(t, shift);
- }
- else {
+ } else {
return _mm256_srli_epi64(q, more);
}
}
}
-__m256i libdivide_u64_branchfree_do_vector(__m256i numers, const struct libdivide_u64_branchfree_t *denom) {
- __m256i q = libdivide_mullhi_u64_vector(numers, _mm256_set1_epi64x(denom->magic));
+__m256i libdivide_u64_branchfree_do_vec256(
+ __m256i numers, const struct libdivide_u64_branchfree_t *denom) {
+ __m256i q = libdivide_mullhi_u64_vec256(numers, _mm256_set1_epi64x(denom->magic));
__m256i t = _mm256_add_epi64(_mm256_srli_epi64(_mm256_sub_epi64(numers, q), 1), q);
return _mm256_srli_epi64(t, denom->more);
}
////////// SINT32
-__m256i libdivide_s32_do_vector(__m256i numers, const struct libdivide_s32_t *denom) {
+__m256i libdivide_s32_do_vec256(__m256i numers, const struct libdivide_s32_t *denom) {
uint8_t more = denom->more;
if (!denom->magic) {
uint32_t shift = more & LIBDIVIDE_32_SHIFT_MASK;
uint32_t mask = (1U << shift) - 1;
__m256i roundToZeroTweak = _mm256_set1_epi32(mask);
// q = numer + ((numer >> 31) & roundToZeroTweak);
- __m256i q = _mm256_add_epi32(numers, _mm256_and_si256(_mm256_srai_epi32(numers, 31), roundToZeroTweak));
+ __m256i q = _mm256_add_epi32(
+ numers, _mm256_and_si256(_mm256_srai_epi32(numers, 31), roundToZeroTweak));
q = _mm256_srai_epi32(q, shift);
__m256i sign = _mm256_set1_epi32((int8_t)more >> 7);
// q = (q ^ sign) - sign;
q = _mm256_sub_epi32(_mm256_xor_si256(q, sign), sign);
return q;
- }
- else {
- __m256i q = libdivide_mullhi_s32_vector(numers, _mm256_set1_epi32(denom->magic));
+ } else {
+ __m256i q = libdivide_mullhi_s32_vec256(numers, _mm256_set1_epi32(denom->magic));
if (more & LIBDIVIDE_ADD_MARKER) {
- // must be arithmetic shift
+ // must be arithmetic shift
__m256i sign = _mm256_set1_epi32((int8_t)more >> 7);
- // q += ((numer ^ sign) - sign);
+ // q += ((numer ^ sign) - sign);
q = _mm256_add_epi32(q, _mm256_sub_epi32(_mm256_xor_si256(numers, sign), sign));
}
// q >>= shift
q = _mm256_srai_epi32(q, more & LIBDIVIDE_32_SHIFT_MASK);
- q = _mm256_add_epi32(q, _mm256_srli_epi32(q, 31)); // q += (q < 0)
+ q = _mm256_add_epi32(q, _mm256_srli_epi32(q, 31)); // q += (q < 0)
return q;
}
}
-__m256i libdivide_s32_branchfree_do_vector(__m256i numers, const struct libdivide_s32_branchfree_t *denom) {
+__m256i libdivide_s32_branchfree_do_vec256(
+ __m256i numers, const struct libdivide_s32_branchfree_t *denom) {
int32_t magic = denom->magic;
uint8_t more = denom->more;
uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK;
- // must be arithmetic shift
+ // must be arithmetic shift
__m256i sign = _mm256_set1_epi32((int8_t)more >> 7);
- __m256i q = libdivide_mullhi_s32_vector(numers, _mm256_set1_epi32(magic));
- q = _mm256_add_epi32(q, numers); // q += numers
+ __m256i q = libdivide_mullhi_s32_vec256(numers, _mm256_set1_epi32(magic));
+ q = _mm256_add_epi32(q, numers); // q += numers
// If q is non-negative, we have nothing to do
// If q is negative, we want to add either (2**shift)-1 if d is
// a power of 2, or (2**shift) if it is not a power of 2
uint32_t is_power_of_2 = (magic == 0);
- __m256i q_sign = _mm256_srai_epi32(q, 31); // q_sign = q >> 31
+ __m256i q_sign = _mm256_srai_epi32(q, 31); // q_sign = q >> 31
__m256i mask = _mm256_set1_epi32((1U << shift) - is_power_of_2);
- q = _mm256_add_epi32(q, _mm256_and_si256(q_sign, mask)); // q = q + (q_sign & mask)
- q = _mm256_srai_epi32(q, shift); // q >>= shift
- q = _mm256_sub_epi32(_mm256_xor_si256(q, sign), sign); // q = (q ^ sign) - sign
+ q = _mm256_add_epi32(q, _mm256_and_si256(q_sign, mask)); // q = q + (q_sign & mask)
+ q = _mm256_srai_epi32(q, shift); // q >>= shift
+ q = _mm256_sub_epi32(_mm256_xor_si256(q, sign), sign); // q = (q ^ sign) - sign
return q;
}
////////// SINT64
-__m256i libdivide_s64_do_vector(__m256i numers, const struct libdivide_s64_t *denom) {
+__m256i libdivide_s64_do_vec256(__m256i numers, const struct libdivide_s64_t *denom) {
uint8_t more = denom->more;
int64_t magic = denom->magic;
- if (magic == 0) { // shift path
+ if (magic == 0) { // shift path
uint32_t shift = more & LIBDIVIDE_64_SHIFT_MASK;
uint64_t mask = (1ULL << shift) - 1;
__m256i roundToZeroTweak = _mm256_set1_epi64x(mask);
// q = numer + ((numer >> 63) & roundToZeroTweak);
- __m256i q = _mm256_add_epi64(numers, _mm256_and_si256(libdivide_s64_signbits(numers), roundToZeroTweak));
- q = libdivide_s64_shift_right_vector(q, shift);
+ __m256i q = _mm256_add_epi64(
+ numers, _mm256_and_si256(libdivide_s64_signbits(numers), roundToZeroTweak));
+ q = libdivide_s64_shift_right_vec256(q, shift);
__m256i sign = _mm256_set1_epi32((int8_t)more >> 7);
- // q = (q ^ sign) - sign;
+ // q = (q ^ sign) - sign;
q = _mm256_sub_epi64(_mm256_xor_si256(q, sign), sign);
return q;
- }
- else {
- __m256i q = libdivide_mullhi_s64_vector(numers, _mm256_set1_epi64x(magic));
+ } else {
+ __m256i q = libdivide_mullhi_s64_vec256(numers, _mm256_set1_epi64x(magic));
if (more & LIBDIVIDE_ADD_MARKER) {
// must be arithmetic shift
__m256i sign = _mm256_set1_epi32((int8_t)more >> 7);
@@ -1655,46 +1931,53 @@ __m256i libdivide_s64_do_vector(__m256i numers, const struct libdivide_s64_t *de
q = _mm256_add_epi64(q, _mm256_sub_epi64(_mm256_xor_si256(numers, sign), sign));
}
// q >>= denom->mult_path.shift
- q = libdivide_s64_shift_right_vector(q, more & LIBDIVIDE_64_SHIFT_MASK);
- q = _mm256_add_epi64(q, _mm256_srli_epi64(q, 63)); // q += (q < 0)
+ q = libdivide_s64_shift_right_vec256(q, more & LIBDIVIDE_64_SHIFT_MASK);
+ q = _mm256_add_epi64(q, _mm256_srli_epi64(q, 63)); // q += (q < 0)
return q;
}
}
-__m256i libdivide_s64_branchfree_do_vector(__m256i numers, const struct libdivide_s64_branchfree_t *denom) {
+__m256i libdivide_s64_branchfree_do_vec256(
+ __m256i numers, const struct libdivide_s64_branchfree_t *denom) {
int64_t magic = denom->magic;
uint8_t more = denom->more;
uint8_t shift = more & LIBDIVIDE_64_SHIFT_MASK;
// must be arithmetic shift
__m256i sign = _mm256_set1_epi32((int8_t)more >> 7);
- // libdivide_mullhi_s64(numers, magic);
- __m256i q = libdivide_mullhi_s64_vector(numers, _mm256_set1_epi64x(magic));
- q = _mm256_add_epi64(q, numers); // q += numers
+ // libdivide_mullhi_s64(numers, magic);
+ __m256i q = libdivide_mullhi_s64_vec256(numers, _mm256_set1_epi64x(magic));
+ q = _mm256_add_epi64(q, numers); // q += numers
// If q is non-negative, we have nothing to do.
// If q is negative, we want to add either (2**shift)-1 if d is
// a power of 2, or (2**shift) if it is not a power of 2.
uint32_t is_power_of_2 = (magic == 0);
- __m256i q_sign = libdivide_s64_signbits(q); // q_sign = q >> 63
+ __m256i q_sign = libdivide_s64_signbits(q); // q_sign = q >> 63
__m256i mask = _mm256_set1_epi64x((1ULL << shift) - is_power_of_2);
- q = _mm256_add_epi64(q, _mm256_and_si256(q_sign, mask)); // q = q + (q_sign & mask)
- q = libdivide_s64_shift_right_vector(q, shift); // q >>= shift
- q = _mm256_sub_epi64(_mm256_xor_si256(q, sign), sign); // q = (q ^ sign) - sign
+ q = _mm256_add_epi64(q, _mm256_and_si256(q_sign, mask)); // q = q + (q_sign & mask)
+ q = libdivide_s64_shift_right_vec256(q, shift); // q >>= shift
+ q = _mm256_sub_epi64(_mm256_xor_si256(q, sign), sign); // q = (q ^ sign) - sign
return q;
}
-#elif defined(LIBDIVIDE_SSE2)
+#endif
-static inline __m128i libdivide_u32_do_vector(__m128i numers, const struct libdivide_u32_t *denom);
-static inline __m128i libdivide_s32_do_vector(__m128i numers, const struct libdivide_s32_t *denom);
-static inline __m128i libdivide_u64_do_vector(__m128i numers, const struct libdivide_u64_t *denom);
-static inline __m128i libdivide_s64_do_vector(__m128i numers, const struct libdivide_s64_t *denom);
+#if defined(LIBDIVIDE_SSE2)
-static inline __m128i libdivide_u32_branchfree_do_vector(__m128i numers, const struct libdivide_u32_branchfree_t *denom);
-static inline __m128i libdivide_s32_branchfree_do_vector(__m128i numers, const struct libdivide_s32_branchfree_t *denom);
-static inline __m128i libdivide_u64_branchfree_do_vector(__m128i numers, const struct libdivide_u64_branchfree_t *denom);
-static inline __m128i libdivide_s64_branchfree_do_vector(__m128i numers, const struct libdivide_s64_branchfree_t *denom);
+static inline __m128i libdivide_u32_do_vec128(__m128i numers, const struct libdivide_u32_t *denom);
+static inline __m128i libdivide_s32_do_vec128(__m128i numers, const struct libdivide_s32_t *denom);
+static inline __m128i libdivide_u64_do_vec128(__m128i numers, const struct libdivide_u64_t *denom);
+static inline __m128i libdivide_s64_do_vec128(__m128i numers, const struct libdivide_s64_t *denom);
+
+static inline __m128i libdivide_u32_branchfree_do_vec128(
+ __m128i numers, const struct libdivide_u32_branchfree_t *denom);
+static inline __m128i libdivide_s32_branchfree_do_vec128(
+ __m128i numers, const struct libdivide_s32_branchfree_t *denom);
+static inline __m128i libdivide_u64_branchfree_do_vec128(
+ __m128i numers, const struct libdivide_u64_branchfree_t *denom);
+static inline __m128i libdivide_s64_branchfree_do_vec128(
+ __m128i numers, const struct libdivide_s64_branchfree_t *denom);
//////// Internal Utility Functions
@@ -1706,7 +1989,7 @@ static inline __m128i libdivide_s64_signbits(__m128i v) {
}
// Implementation of _mm_srai_epi64 (from AVX512).
-static inline __m128i libdivide_s64_shift_right_vector(__m128i v, int amt) {
+static inline __m128i libdivide_s64_shift_right_vec128(__m128i v, int amt) {
const int b = 64 - amt;
__m128i m = _mm_set1_epi64x(1ULL << (b - 1));
__m128i x = _mm_srli_epi64(v, amt);
@@ -1715,7 +1998,7 @@ static inline __m128i libdivide_s64_shift_right_vector(__m128i v, int amt) {
}
// Here, b is assumed to contain one 32-bit value repeated.
-static inline __m128i libdivide_mullhi_u32_vector(__m128i a, __m128i b) {
+static inline __m128i libdivide_mullhi_u32_vec128(__m128i a, __m128i b) {
__m128i hi_product_0Z2Z = _mm_srli_epi64(_mm_mul_epu32(a, b), 32);
__m128i a1X3X = _mm_srli_epi64(a, 32);
__m128i mask = _mm_set_epi32(-1, 0, -1, 0);
@@ -1726,8 +2009,8 @@ static inline __m128i libdivide_mullhi_u32_vector(__m128i a, __m128i b) {
// SSE2 does not have a signed multiplication instruction, but we can convert
// unsigned to signed pretty efficiently. Again, b is just a 32 bit value
// repeated four times.
-static inline __m128i libdivide_mullhi_s32_vector(__m128i a, __m128i b) {
- __m128i p = libdivide_mullhi_u32_vector(a, b);
+static inline __m128i libdivide_mullhi_s32_vec128(__m128i a, __m128i b) {
+ __m128i p = libdivide_mullhi_u32_vec128(a, b);
// t1 = (a >> 31) & y, arithmetic shift
__m128i t1 = _mm_and_si128(_mm_srai_epi32(a, 31), b);
__m128i t2 = _mm_and_si128(_mm_srai_epi32(b, 31), a);
@@ -1737,30 +2020,41 @@ static inline __m128i libdivide_mullhi_s32_vector(__m128i a, __m128i b) {
}
// Here, y is assumed to contain one 64-bit value repeated.
-// https://stackoverflow.com/a/28827013
-static inline __m128i libdivide_mullhi_u64_vector(__m128i x, __m128i y) {
- __m128i lomask = _mm_set1_epi64x(0xffffffff);
- __m128i xh = _mm_shuffle_epi32(x, 0xB1); // x0l, x0h, x1l, x1h
- __m128i yh = _mm_shuffle_epi32(y, 0xB1); // y0l, y0h, y1l, y1h
- __m128i w0 = _mm_mul_epu32(x, y); // x0l*y0l, x1l*y1l
- __m128i w1 = _mm_mul_epu32(x, yh); // x0l*y0h, x1l*y1h
- __m128i w2 = _mm_mul_epu32(xh, y); // x0h*y0l, x1h*y0l
- __m128i w3 = _mm_mul_epu32(xh, yh); // x0h*y0h, x1h*y1h
- __m128i w0h = _mm_srli_epi64(w0, 32);
- __m128i s1 = _mm_add_epi64(w1, w0h);
- __m128i s1l = _mm_and_si128(s1, lomask);
- __m128i s1h = _mm_srli_epi64(s1, 32);
- __m128i s2 = _mm_add_epi64(w2, s1l);
- __m128i s2h = _mm_srli_epi64(s2, 32);
- __m128i hi = _mm_add_epi64(w3, s1h);
- hi = _mm_add_epi64(hi, s2h);
+static inline __m128i libdivide_mullhi_u64_vec128(__m128i x, __m128i y) {
+ // full 128 bits product is:
+ // x0*y0 + (x0*y1 << 32) + (x1*y0 << 32) + (x1*y1 << 64)
+ // Note x0,y0,x1,y1 are all conceptually uint32, products are 32x32->64.
- return hi;
+ // Compute x0*y0.
+ // Note x1, y1 are ignored by mul_epu32.
+ __m128i x0y0 = _mm_mul_epu32(x, y);
+ __m128i x0y0_hi = _mm_srli_epi64(x0y0, 32);
+
+ // Get x1, y1 in the low bits.
+ // We could shuffle or right shift. Shuffles are preferred as they preserve
+ // the source register for the next computation.
+ __m128i x1 = _mm_shuffle_epi32(x, _MM_SHUFFLE(3, 3, 1, 1));
+ __m128i y1 = _mm_shuffle_epi32(y, _MM_SHUFFLE(3, 3, 1, 1));
+
+ // No need to mask off top 32 bits for mul_epu32.
+ __m128i x0y1 = _mm_mul_epu32(x, y1);
+ __m128i x1y0 = _mm_mul_epu32(x1, y);
+ __m128i x1y1 = _mm_mul_epu32(x1, y1);
+
+ // Mask here selects low bits only.
+ __m128i mask = _mm_set1_epi64x(0xFFFFFFFF);
+ __m128i temp = _mm_add_epi64(x1y0, x0y0_hi);
+ __m128i temp_lo = _mm_and_si128(temp, mask);
+ __m128i temp_hi = _mm_srli_epi64(temp, 32);
+
+ temp_lo = _mm_srli_epi64(_mm_add_epi64(temp_lo, x0y1), 32);
+ temp_hi = _mm_add_epi64(x1y1, temp_hi);
+ return _mm_add_epi64(temp_lo, temp_hi);
}
// y is one 64-bit value repeated.
-static inline __m128i libdivide_mullhi_s64_vector(__m128i x, __m128i y) {
- __m128i p = libdivide_mullhi_u64_vector(x, y);
+static inline __m128i libdivide_mullhi_s64_vec128(__m128i x, __m128i y) {
+ __m128i p = libdivide_mullhi_u64_vec128(x, y);
__m128i t1 = _mm_and_si128(libdivide_s64_signbits(x), y);
__m128i t2 = _mm_and_si128(libdivide_s64_signbits(y), x);
p = _mm_sub_epi64(p, t1);
@@ -1770,131 +2064,130 @@ static inline __m128i libdivide_mullhi_s64_vector(__m128i x, __m128i y) {
////////// UINT32
-__m128i libdivide_u32_do_vector(__m128i numers, const struct libdivide_u32_t *denom) {
+__m128i libdivide_u32_do_vec128(__m128i numers, const struct libdivide_u32_t *denom) {
uint8_t more = denom->more;
if (!denom->magic) {
return _mm_srli_epi32(numers, more);
- }
- else {
- __m128i q = libdivide_mullhi_u32_vector(numers, _mm_set1_epi32(denom->magic));
+ } else {
+ __m128i q = libdivide_mullhi_u32_vec128(numers, _mm_set1_epi32(denom->magic));
if (more & LIBDIVIDE_ADD_MARKER) {
// uint32_t t = ((numer - q) >> 1) + q;
// return t >> denom->shift;
uint32_t shift = more & LIBDIVIDE_32_SHIFT_MASK;
__m128i t = _mm_add_epi32(_mm_srli_epi32(_mm_sub_epi32(numers, q), 1), q);
return _mm_srli_epi32(t, shift);
- }
- else {
+ } else {
return _mm_srli_epi32(q, more);
}
}
}
-__m128i libdivide_u32_branchfree_do_vector(__m128i numers, const struct libdivide_u32_branchfree_t *denom) {
- __m128i q = libdivide_mullhi_u32_vector(numers, _mm_set1_epi32(denom->magic));
+__m128i libdivide_u32_branchfree_do_vec128(
+ __m128i numers, const struct libdivide_u32_branchfree_t *denom) {
+ __m128i q = libdivide_mullhi_u32_vec128(numers, _mm_set1_epi32(denom->magic));
__m128i t = _mm_add_epi32(_mm_srli_epi32(_mm_sub_epi32(numers, q), 1), q);
return _mm_srli_epi32(t, denom->more);
}
////////// UINT64
-__m128i libdivide_u64_do_vector(__m128i numers, const struct libdivide_u64_t *denom) {
+__m128i libdivide_u64_do_vec128(__m128i numers, const struct libdivide_u64_t *denom) {
uint8_t more = denom->more;
if (!denom->magic) {
return _mm_srli_epi64(numers, more);
- }
- else {
- __m128i q = libdivide_mullhi_u64_vector(numers, _mm_set1_epi64x(denom->magic));
+ } else {
+ __m128i q = libdivide_mullhi_u64_vec128(numers, _mm_set1_epi64x(denom->magic));
if (more & LIBDIVIDE_ADD_MARKER) {
// uint32_t t = ((numer - q) >> 1) + q;
// return t >> denom->shift;
uint32_t shift = more & LIBDIVIDE_64_SHIFT_MASK;
__m128i t = _mm_add_epi64(_mm_srli_epi64(_mm_sub_epi64(numers, q), 1), q);
return _mm_srli_epi64(t, shift);
- }
- else {
+ } else {
return _mm_srli_epi64(q, more);
}
}
}
-__m128i libdivide_u64_branchfree_do_vector(__m128i numers, const struct libdivide_u64_branchfree_t *denom) {
- __m128i q = libdivide_mullhi_u64_vector(numers, _mm_set1_epi64x(denom->magic));
+__m128i libdivide_u64_branchfree_do_vec128(
+ __m128i numers, const struct libdivide_u64_branchfree_t *denom) {
+ __m128i q = libdivide_mullhi_u64_vec128(numers, _mm_set1_epi64x(denom->magic));
__m128i t = _mm_add_epi64(_mm_srli_epi64(_mm_sub_epi64(numers, q), 1), q);
return _mm_srli_epi64(t, denom->more);
}
////////// SINT32
-__m128i libdivide_s32_do_vector(__m128i numers, const struct libdivide_s32_t *denom) {
+__m128i libdivide_s32_do_vec128(__m128i numers, const struct libdivide_s32_t *denom) {
uint8_t more = denom->more;
if (!denom->magic) {
uint32_t shift = more & LIBDIVIDE_32_SHIFT_MASK;
uint32_t mask = (1U << shift) - 1;
__m128i roundToZeroTweak = _mm_set1_epi32(mask);
// q = numer + ((numer >> 31) & roundToZeroTweak);
- __m128i q = _mm_add_epi32(numers, _mm_and_si128(_mm_srai_epi32(numers, 31), roundToZeroTweak));
+ __m128i q =
+ _mm_add_epi32(numers, _mm_and_si128(_mm_srai_epi32(numers, 31), roundToZeroTweak));
q = _mm_srai_epi32(q, shift);
__m128i sign = _mm_set1_epi32((int8_t)more >> 7);
// q = (q ^ sign) - sign;
q = _mm_sub_epi32(_mm_xor_si128(q, sign), sign);
return q;
- }
- else {
- __m128i q = libdivide_mullhi_s32_vector(numers, _mm_set1_epi32(denom->magic));
+ } else {
+ __m128i q = libdivide_mullhi_s32_vec128(numers, _mm_set1_epi32(denom->magic));
if (more & LIBDIVIDE_ADD_MARKER) {
- // must be arithmetic shift
+ // must be arithmetic shift
__m128i sign = _mm_set1_epi32((int8_t)more >> 7);
- // q += ((numer ^ sign) - sign);
+ // q += ((numer ^ sign) - sign);
q = _mm_add_epi32(q, _mm_sub_epi32(_mm_xor_si128(numers, sign), sign));
}
// q >>= shift
q = _mm_srai_epi32(q, more & LIBDIVIDE_32_SHIFT_MASK);
- q = _mm_add_epi32(q, _mm_srli_epi32(q, 31)); // q += (q < 0)
+ q = _mm_add_epi32(q, _mm_srli_epi32(q, 31)); // q += (q < 0)
return q;
}
}
-__m128i libdivide_s32_branchfree_do_vector(__m128i numers, const struct libdivide_s32_branchfree_t *denom) {
+__m128i libdivide_s32_branchfree_do_vec128(
+ __m128i numers, const struct libdivide_s32_branchfree_t *denom) {
int32_t magic = denom->magic;
uint8_t more = denom->more;
uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK;
- // must be arithmetic shift
+ // must be arithmetic shift
__m128i sign = _mm_set1_epi32((int8_t)more >> 7);
- __m128i q = libdivide_mullhi_s32_vector(numers, _mm_set1_epi32(magic));
- q = _mm_add_epi32(q, numers); // q += numers
+ __m128i q = libdivide_mullhi_s32_vec128(numers, _mm_set1_epi32(magic));
+ q = _mm_add_epi32(q, numers); // q += numers
// If q is non-negative, we have nothing to do
// If q is negative, we want to add either (2**shift)-1 if d is
// a power of 2, or (2**shift) if it is not a power of 2
uint32_t is_power_of_2 = (magic == 0);
- __m128i q_sign = _mm_srai_epi32(q, 31); // q_sign = q >> 31
+ __m128i q_sign = _mm_srai_epi32(q, 31); // q_sign = q >> 31
__m128i mask = _mm_set1_epi32((1U << shift) - is_power_of_2);
- q = _mm_add_epi32(q, _mm_and_si128(q_sign, mask)); // q = q + (q_sign & mask)
- q = _mm_srai_epi32(q, shift); // q >>= shift
- q = _mm_sub_epi32(_mm_xor_si128(q, sign), sign); // q = (q ^ sign) - sign
+ q = _mm_add_epi32(q, _mm_and_si128(q_sign, mask)); // q = q + (q_sign & mask)
+ q = _mm_srai_epi32(q, shift); // q >>= shift
+ q = _mm_sub_epi32(_mm_xor_si128(q, sign), sign); // q = (q ^ sign) - sign
return q;
}
////////// SINT64
-__m128i libdivide_s64_do_vector(__m128i numers, const struct libdivide_s64_t *denom) {
+__m128i libdivide_s64_do_vec128(__m128i numers, const struct libdivide_s64_t *denom) {
uint8_t more = denom->more;
int64_t magic = denom->magic;
- if (magic == 0) { // shift path
+ if (magic == 0) { // shift path
uint32_t shift = more & LIBDIVIDE_64_SHIFT_MASK;
uint64_t mask = (1ULL << shift) - 1;
__m128i roundToZeroTweak = _mm_set1_epi64x(mask);
// q = numer + ((numer >> 63) & roundToZeroTweak);
- __m128i q = _mm_add_epi64(numers, _mm_and_si128(libdivide_s64_signbits(numers), roundToZeroTweak));
- q = libdivide_s64_shift_right_vector(q, shift);
+ __m128i q =
+ _mm_add_epi64(numers, _mm_and_si128(libdivide_s64_signbits(numers), roundToZeroTweak));
+ q = libdivide_s64_shift_right_vec128(q, shift);
__m128i sign = _mm_set1_epi32((int8_t)more >> 7);
- // q = (q ^ sign) - sign;
+ // q = (q ^ sign) - sign;
q = _mm_sub_epi64(_mm_xor_si128(q, sign), sign);
return q;
- }
- else {
- __m128i q = libdivide_mullhi_s64_vector(numers, _mm_set1_epi64x(magic));
+ } else {
+ __m128i q = libdivide_mullhi_s64_vec128(numers, _mm_set1_epi64x(magic));
if (more & LIBDIVIDE_ADD_MARKER) {
// must be arithmetic shift
__m128i sign = _mm_set1_epi32((int8_t)more >> 7);
@@ -1902,32 +2195,33 @@ __m128i libdivide_s64_do_vector(__m128i numers, const struct libdivide_s64_t *de
q = _mm_add_epi64(q, _mm_sub_epi64(_mm_xor_si128(numers, sign), sign));
}
// q >>= denom->mult_path.shift
- q = libdivide_s64_shift_right_vector(q, more & LIBDIVIDE_64_SHIFT_MASK);
- q = _mm_add_epi64(q, _mm_srli_epi64(q, 63)); // q += (q < 0)
+ q = libdivide_s64_shift_right_vec128(q, more & LIBDIVIDE_64_SHIFT_MASK);
+ q = _mm_add_epi64(q, _mm_srli_epi64(q, 63)); // q += (q < 0)
return q;
}
}
-__m128i libdivide_s64_branchfree_do_vector(__m128i numers, const struct libdivide_s64_branchfree_t *denom) {
+__m128i libdivide_s64_branchfree_do_vec128(
+ __m128i numers, const struct libdivide_s64_branchfree_t *denom) {
int64_t magic = denom->magic;
uint8_t more = denom->more;
uint8_t shift = more & LIBDIVIDE_64_SHIFT_MASK;
// must be arithmetic shift
__m128i sign = _mm_set1_epi32((int8_t)more >> 7);
- // libdivide_mullhi_s64(numers, magic);
- __m128i q = libdivide_mullhi_s64_vector(numers, _mm_set1_epi64x(magic));
- q = _mm_add_epi64(q, numers); // q += numers
+ // libdivide_mullhi_s64(numers, magic);
+ __m128i q = libdivide_mullhi_s64_vec128(numers, _mm_set1_epi64x(magic));
+ q = _mm_add_epi64(q, numers); // q += numers
// If q is non-negative, we have nothing to do.
// If q is negative, we want to add either (2**shift)-1 if d is
// a power of 2, or (2**shift) if it is not a power of 2.
uint32_t is_power_of_2 = (magic == 0);
- __m128i q_sign = libdivide_s64_signbits(q); // q_sign = q >> 63
+ __m128i q_sign = libdivide_s64_signbits(q); // q_sign = q >> 63
__m128i mask = _mm_set1_epi64x((1ULL << shift) - is_power_of_2);
- q = _mm_add_epi64(q, _mm_and_si128(q_sign, mask)); // q = q + (q_sign & mask)
- q = libdivide_s64_shift_right_vector(q, shift); // q >>= shift
- q = _mm_sub_epi64(_mm_xor_si128(q, sign), sign); // q = (q ^ sign) - sign
+ q = _mm_add_epi64(q, _mm_and_si128(q_sign, mask)); // q = q + (q_sign & mask)
+ q = libdivide_s64_shift_right_vec128(q, shift); // q >>= shift
+ q = _mm_sub_epi64(_mm_xor_si128(q, sign), sign); // q = (q ^ sign) - sign
return q;
}
@@ -1937,143 +2231,273 @@ __m128i libdivide_s64_branchfree_do_vector(__m128i numers, const struct libdivid
#ifdef __cplusplus
-// The C++ divider class is templated on both an integer type
-// (like uint64_t) and an algorithm type.
-// * BRANCHFULL is the default algorithm type.
-// * BRANCHFREE is the branchfree algorithm type.
-enum {
- BRANCHFULL,
- BRANCHFREE
+enum Branching {
+ BRANCHFULL, // use branching algorithms
+ BRANCHFREE // use branchfree algorithms
};
-#if defined(LIBDIVIDE_AVX512)
- #define LIBDIVIDE_VECTOR_TYPE __m512i
-#elif defined(LIBDIVIDE_AVX2)
- #define LIBDIVIDE_VECTOR_TYPE __m256i
-#elif defined(LIBDIVIDE_SSE2)
- #define LIBDIVIDE_VECTOR_TYPE __m128i
+#if defined(LIBDIVIDE_NEON)
+// Helper to deduce NEON vector type for integral type.
+template
+struct NeonVecFor {};
+
+template <>
+struct NeonVecFor {
+ typedef uint32x4_t type;
+};
+
+template <>
+struct NeonVecFor {
+ typedef int32x4_t type;
+};
+
+template <>
+struct NeonVecFor {
+ typedef uint64x2_t type;
+};
+
+template <>
+struct NeonVecFor {
+ typedef int64x2_t type;
+};
#endif
-#if !defined(LIBDIVIDE_VECTOR_TYPE)
- #define LIBDIVIDE_DIVIDE_VECTOR(ALGO)
+// Versions of our algorithms for SIMD.
+#if defined(LIBDIVIDE_NEON)
+#define LIBDIVIDE_DIVIDE_NEON(ALGO, INT_TYPE) \
+ typename NeonVecFor::type divide(typename NeonVecFor::type n) const { \
+ return libdivide_##ALGO##_do_vec128(n, &denom); \
+ }
#else
- #define LIBDIVIDE_DIVIDE_VECTOR(ALGO) \
- LIBDIVIDE_VECTOR_TYPE divide(LIBDIVIDE_VECTOR_TYPE n) const { \
- return libdivide_##ALGO##_do_vector(n, &denom); \
- }
+#define LIBDIVIDE_DIVIDE_NEON(ALGO, INT_TYPE)
+#endif
+#if defined(LIBDIVIDE_SSE2)
+#define LIBDIVIDE_DIVIDE_SSE2(ALGO) \
+ __m128i divide(__m128i n) const { return libdivide_##ALGO##_do_vec128(n, &denom); }
+#else
+#define LIBDIVIDE_DIVIDE_SSE2(ALGO)
+#endif
+
+#if defined(LIBDIVIDE_AVX2)
+#define LIBDIVIDE_DIVIDE_AVX2(ALGO) \
+ __m256i divide(__m256i n) const { return libdivide_##ALGO##_do_vec256(n, &denom); }
+#else
+#define LIBDIVIDE_DIVIDE_AVX2(ALGO)
+#endif
+
+#if defined(LIBDIVIDE_AVX512)
+#define LIBDIVIDE_DIVIDE_AVX512(ALGO) \
+ __m512i divide(__m512i n) const { return libdivide_##ALGO##_do_vec512(n, &denom); }
+#else
+#define LIBDIVIDE_DIVIDE_AVX512(ALGO)
#endif
// The DISPATCHER_GEN() macro generates C++ methods (for the given integer
// and algorithm types) that redirect to libdivide's C API.
-#define DISPATCHER_GEN(T, ALGO) \
- libdivide_##ALGO##_t denom; \
- dispatcher() { } \
- dispatcher(T d) \
- : denom(libdivide_##ALGO##_gen(d)) \
- { } \
- T divide(T n) const { \
- return libdivide_##ALGO##_do(n, &denom); \
- } \
- LIBDIVIDE_DIVIDE_VECTOR(ALGO) \
- T recover() const { \
- return libdivide_##ALGO##_recover(&denom); \
- }
+#define DISPATCHER_GEN(T, ALGO) \
+ libdivide_##ALGO##_t denom; \
+ dispatcher() {} \
+ dispatcher(T d) : denom(libdivide_##ALGO##_gen(d)) {} \
+ T divide(T n) const { return libdivide_##ALGO##_do(n, &denom); } \
+ T recover() const { return libdivide_##ALGO##_recover(&denom); } \
+ LIBDIVIDE_DIVIDE_NEON(ALGO, T) \
+ LIBDIVIDE_DIVIDE_SSE2(ALGO) \
+ LIBDIVIDE_DIVIDE_AVX2(ALGO) \
+ LIBDIVIDE_DIVIDE_AVX512(ALGO)
// The dispatcher selects a specific division algorithm for a given
// type and ALGO using partial template specialization.
-template struct dispatcher { };
+template
+struct dispatcher {};
-template<> struct dispatcher { DISPATCHER_GEN(int32_t, s32) };
-template<> struct dispatcher { DISPATCHER_GEN(int32_t, s32_branchfree) };
-template<> struct dispatcher { DISPATCHER_GEN(uint32_t, u32) };
-template<> struct dispatcher { DISPATCHER_GEN(uint32_t, u32_branchfree) };
-template<> struct dispatcher { DISPATCHER_GEN(int64_t, s64) };
-template<> struct dispatcher { DISPATCHER_GEN(int64_t, s64_branchfree) };
-template<> struct dispatcher { DISPATCHER_GEN(uint64_t, u64) };
-template<> struct dispatcher { DISPATCHER_GEN(uint64_t, u64_branchfree) };
+template <>
+struct dispatcher {
+ DISPATCHER_GEN(int32_t, s32)
+};
+template <>
+struct dispatcher {
+ DISPATCHER_GEN(int32_t, s32_branchfree)
+};
+template <>
+struct dispatcher {
+ DISPATCHER_GEN(uint32_t, u32)
+};
+template <>
+struct dispatcher {
+ DISPATCHER_GEN(uint32_t, u32_branchfree)
+};
+template <>
+struct dispatcher {
+ DISPATCHER_GEN(int64_t, s64)
+};
+template <>
+struct dispatcher {
+ DISPATCHER_GEN(int64_t, s64_branchfree)
+};
+template <>
+struct dispatcher {
+ DISPATCHER_GEN(uint64_t, u64)
+};
+template <>
+struct dispatcher {
+ DISPATCHER_GEN(uint64_t, u64_branchfree)
+};
// This is the main divider class for use by the user (C++ API).
// The actual division algorithm is selected using the dispatcher struct
// based on the integer and algorithm template parameters.
-template
+template
class divider {
-public:
+ public:
// We leave the default constructor empty so that creating
// an array of dividers and then initializing them
// later doesn't slow us down.
- divider() { }
+ divider() {}
// Constructor that takes the divisor as a parameter
- divider(T d) : div(d) { }
+ divider(T d) : div(d) {}
// Divides n by the divisor
- T divide(T n) const {
- return div.divide(n);
- }
+ T divide(T n) const { return div.divide(n); }
// Recovers the divisor, returns the value that was
// used to initialize this divider object.
- T recover() const {
- return div.recover();
+ T recover() const { return div.recover(); }
+
+ bool operator==(const divider &other) const {
+ return div.denom.magic == other.denom.magic && div.denom.more == other.denom.more;
}
- bool operator==(const divider& other) const {
- return div.denom.magic == other.denom.magic &&
- div.denom.more == other.denom.more;
- }
+ bool operator!=(const divider &other) const { return !(*this == other); }
- bool operator!=(const divider& other) const {
- return !(*this == other);
- }
-
-#if defined(LIBDIVIDE_VECTOR_TYPE)
- // Treats the vector as packed integer values with the same type as
- // the divider (e.g. s32, u32, s64, u64) and divides each of
- // them by the divider, returning the packed quotients.
- LIBDIVIDE_VECTOR_TYPE divide(LIBDIVIDE_VECTOR_TYPE n) const {
+ // Vector variants treat the input as packed integer values with the same type as the divider
+ // (e.g. s32, u32, s64, u64) and divides each of them by the divider, returning the packed
+ // quotients.
+#if defined(LIBDIVIDE_SSE2)
+ __m128i divide(__m128i n) const { return div.divide(n); }
+#endif
+#if defined(LIBDIVIDE_AVX2)
+ __m256i divide(__m256i n) const { return div.divide(n); }
+#endif
+#if defined(LIBDIVIDE_AVX512)
+ __m512i divide(__m512i n) const { return div.divide(n); }
+#endif
+#if defined(LIBDIVIDE_NEON)
+ typename NeonVecFor::type divide(typename NeonVecFor::type n) const {
return div.divide(n);
}
#endif
-private:
+ private:
// Storage for the actual divisor
- dispatcher::value,
- std::is_signed::value, sizeof(T), ALGO> div;
+ dispatcher::value, std::is_signed::value, sizeof(T), ALGO> div;
};
// Overload of operator / for scalar division
-template
-T operator/(T n, const divider& div) {
+template
+T operator/(T n, const divider &div) {
return div.divide(n);
}
// Overload of operator /= for scalar division
-template
-T& operator/=(T& n, const divider& div) {
+template
+T &operator/=(T &n, const divider &div) {
n = div.divide(n);
return n;
}
-#if defined(LIBDIVIDE_VECTOR_TYPE)
- // Overload of operator / for vector division
- template
- LIBDIVIDE_VECTOR_TYPE operator/(LIBDIVIDE_VECTOR_TYPE n, const divider& div) {
- return div.divide(n);
- }
- // Overload of operator /= for vector division
- template
- LIBDIVIDE_VECTOR_TYPE& operator/=(LIBDIVIDE_VECTOR_TYPE& n, const divider& div) {
- n = div.divide(n);
- return n;
- }
+// Overloads for vector types.
+#if defined(LIBDIVIDE_SSE2)
+template
+__m128i operator/(__m128i n, const divider &div) {
+ return div.divide(n);
+}
+
+template
+__m128i operator/=(__m128i &n, const divider &div) {
+ n = div.divide(n);
+ return n;
+}
+#endif
+#if defined(LIBDIVIDE_AVX2)
+template
+__m256i operator/(__m256i n, const divider &div) {
+ return div.divide(n);
+}
+
+template
+__m256i operator/=(__m256i &n, const divider &div) {
+ n = div.divide(n);
+ return n;
+}
+#endif
+#if defined(LIBDIVIDE_AVX512)
+template
+__m512i operator/(__m512i n, const divider &div) {
+ return div.divide(n);
+}
+
+template
+__m512i operator/=(__m512i &n, const divider &div) {
+ n = div.divide(n);
+ return n;
+}
#endif
-// libdivdie::branchfree_divider
+#if defined(LIBDIVIDE_NEON)
+template
+uint32x4_t operator/(uint32x4_t n, const divider &div) {
+ return div.divide(n);
+}
+
+template
+int32x4_t operator/(int32x4_t n, const divider &div) {
+ return div.divide(n);
+}
+
+template
+uint64x2_t operator/(uint64x2_t n, const divider &div) {
+ return div.divide(n);
+}
+
+template
+int64x2_t operator/(int64x2_t n, const divider