├── .editorconfig ├── .github └── workflows │ └── main.yml ├── .gitignore ├── CHANGELOG.md ├── Emakefile ├── LICENSE ├── Makefile ├── README.adoc ├── doc └── overview.edoc ├── ebin └── .gitignore ├── rebar.config ├── rebar.lock ├── src ├── uef.app.src ├── uef_bin.erl ├── uef_crypt.erl ├── uef_encode.erl ├── uef_file.erl ├── uef_format.erl ├── uef_lists.erl ├── uef_maps.erl ├── uef_num.erl └── uef_time.erl └── test ├── data └── uef_file_1.txt ├── uef_bin_tests.erl ├── uef_crypt_tests.erl ├── uef_encode_tests.erl ├── uef_file_tests.erl ├── uef_format_tests.erl ├── uef_lists_tests.erl ├── uef_maps_tests.erl ├── uef_num_tests.erl └── uef_time_tests.erl /.editorconfig: -------------------------------------------------------------------------------- 1 | # https://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | indent_style = space 7 | indent_size = 4 8 | charset = utf-8 9 | end_of_line = lf 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | 13 | [*.md] 14 | indent_style = space 15 | indent_size = 4 16 | 17 | [*.yml] 18 | indent_style = space 19 | indent_size = 2 20 | 21 | [Makefile] 22 | indent_style = tab 23 | indent_size = 4 24 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | - devel 8 | pull_request: 9 | branches: 10 | - master 11 | - devel 12 | 13 | jobs: 14 | linux: 15 | name: Build (${{ matrix.os }}, OTP-${{ matrix.otp_version }}) 16 | runs-on: ${{ matrix.os }} 17 | defaults: 18 | run: 19 | shell: bash 20 | 21 | strategy: 22 | matrix: 23 | otp_version: [20, 21, 22, 23, 24, 25, 26, 27] 24 | os: [ubuntu-latest] 25 | 26 | container: 27 | image: erlang:${{ matrix.otp_version }} 28 | 29 | steps: 30 | - name: Checkout 31 | uses: actions/checkout@v4 32 | - name: Get branch name 33 | id: branch-name 34 | shell: bash 35 | run: echo "branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> $GITHUB_OUTPUT 36 | - name: Cache Dialyzer PLTs 37 | uses: actions/cache@v3 38 | with: 39 | path: | 40 | ~/.cache/rebar3/rebar3_*_plt 41 | _build/default/rebar3_*_plt 42 | key: ${{ runner.os }}-${{ steps.branch-name.outputs.branch }}-otp-${{ matrix.otp_version }}-plt-${{ hashFiles(format('{0}{1}', github.workspace, '/rebar.config')) }} 43 | restore-keys: | 44 | ${{ runner.os }}-${{ steps.branch-name.outputs.branch }}-otp-${{ matrix.otp_version }}-plt- 45 | - name: Cache Hex packages 46 | uses: actions/cache@v3 47 | with: 48 | path: ~/.cache/rebar3/hex/hexpm/packages 49 | key: ${{ runner.os }}-${{ steps.branch-name.outputs.branch }}-otp-${{ matrix.otp_version }}-hex-${{ hashFiles(format('{0}{1}', github.workspace, '/rebar.lock')) }} 50 | restore-keys: | 51 | ${{ runner.os }}-${{ steps.branch-name.outputs.branch }}-otp-${{ matrix.otp_version }}-hex- 52 | - name: Compile 53 | run: rebar3 compile 54 | - name: EUnit tests 55 | run: rebar3 eunit 56 | - name: Dialyzer 57 | run: rebar3 dialyzer 58 | - name: Code coverage 59 | run: rebar3 cover 60 | - name: Generate coverage report 61 | run: rebar3 covertool generate 62 | - name: Upload coverage to Codecov 63 | run: bash <(curl -s https://codecov.io/bash) -f _build/test/covertool/uef.covertool.xml 64 | 65 | 66 | macos: 67 | name: Build (${{ matrix.os }}) 68 | runs-on: ${{ matrix.os }} 69 | defaults: 70 | run: 71 | shell: bash 72 | 73 | strategy: 74 | matrix: 75 | os: [macos-latest] 76 | 77 | steps: 78 | - name: Checkout 79 | uses: actions/checkout@v4 80 | - name: Get branch name 81 | id: branch-name 82 | shell: bash 83 | run: echo "branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> $GITHUB_OUTPUT 84 | - name: Install Erlang 85 | run: brew install erlang 86 | - name: Install rebar3 87 | run: brew install rebar3 88 | - name: Cache Dialyzer PLTs 89 | uses: actions/cache@v3 90 | with: 91 | path: | 92 | ~/.cache/rebar3/rebar3_*_plt 93 | _build/default/rebar3_*_plt 94 | key: ${{ runner.os }}-${{ steps.branch-name.outputs.branch }}-plt-${{ hashFiles(format('{0}{1}', github.workspace, '/rebar.config')) }} 95 | restore-keys: | 96 | ${{ runner.os }}-${{ steps.branch-name.outputs.branch }}-plt- 97 | - name: Cache Hex packages 98 | uses: actions/cache@v3 99 | with: 100 | path: ~/.cache/rebar3/hex/hexpm/packages 101 | key: ${{ runner.os }}-${{ steps.branch-name.outputs.branch }}-hex-${{ hashFiles(format('{0}{1}', github.workspace, '/rebar.lock')) }} 102 | restore-keys: | 103 | ${{ runner.os }}-${{ steps.branch-name.outputs.branch }}-hex- 104 | - name: Compile 105 | run: rebar3 compile 106 | - name: EUnit tests 107 | run: rebar3 eunit 108 | - name: Code coverage 109 | run: rebar3 cover 110 | 111 | 112 | windows: 113 | name: Build (${{ matrix.os }}) 114 | runs-on: ${{ matrix.os }} 115 | defaults: 116 | run: 117 | shell: powershell 118 | 119 | strategy: 120 | matrix: 121 | os: [windows-latest] 122 | steps: 123 | - name: Checkout 124 | uses: actions/checkout@v4 125 | - name: Get branch name 126 | id: branch-name 127 | shell: bash 128 | run: echo "branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> $GITHUB_OUTPUT 129 | - name: Install Erlang 130 | run: choco install erlang -y 131 | - name: Install rebar3 132 | run: | 133 | mkdir ~/rebar3-dl 134 | Invoke-WebRequest -Uri https://github.com/erlang/rebar3/releases/download/3.22.1/rebar3 -OutFile ~/rebar3-dl/rebar3 135 | - name: Cache Dialyzer PLTs 136 | uses: actions/cache@v3 137 | with: 138 | path: | 139 | ~/.cache/rebar3/rebar3_*_plt 140 | _build/default/rebar3_*_plt 141 | key: ${{ runner.os }}-${{ steps.branch-name.outputs.branch }}-plt-${{ hashFiles(format('{0}{1}', github.workspace, '/rebar.config')) }} 142 | restore-keys: | 143 | ${{ runner.os }}-${{ steps.branch-name.outputs.branch }}-plt- 144 | - name: Cache Hex packages 145 | uses: actions/cache@v3 146 | with: 147 | path: ~/.cache/rebar3/hex/hexpm/packages 148 | key: ${{ runner.os }}-${{ steps.branch-name.outputs.branch }}-hex-${{ hashFiles(format('{0}{1}', github.workspace, '/rebar.lock')) }} 149 | restore-keys: | 150 | ${{ runner.os }}-${{ steps.branch-name.outputs.branch }}-hex- 151 | - name: Compile 152 | shell: bash 153 | run: ~/rebar3-dl/rebar3 compile 154 | - name: EUnit tests 155 | shell: bash 156 | run: ~/rebar3-dl/rebar3 eunit 157 | - name: Dialyzer 158 | shell: bash 159 | run: ~/rebar3-dl/rebar3 dialyzer 160 | - name: Code coverage 161 | shell: bash 162 | run: ~/rebar3-dl/rebar3 cover 163 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # rebar3 2 | _build 3 | doc/* 4 | !doc/overview.edoc 5 | # macOS annoying file 6 | .DS_Store 7 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | ## [2.6.0] - 2020-10-12 6 | 7 | - New function `uef_bin:chomp/1`. 8 | - New function `uef_bin:strip_left/2`. 9 | - New function `uef_bin:strip_right/2`. 10 | - New function `uef_bin:strip_both/2`. 11 | 12 | ## [2.5.3] - 2020-01-09 13 | 14 | - Fixed: test of function `uef_time:add_months/2` not passed for leap year. 15 | 16 | ## [2.5.2] - 2019-11-15 17 | 18 | - License changed to Apache 2.0. 19 | - README converted to asciidoc format. 20 | 21 | ## [2.5.1] - 2019-09-26 22 | 23 | - Some binary optimizations in `uef_bin` and `uef_format` modules. 24 | 25 | ## [2.5.0] - 2019-08-09 26 | 27 | - Separator option `sep` added for function `uef_format:format_bytes/2`. 28 | 29 | ## [2.4.0] - 2019-07-28 30 | 31 | - New function `uef_lists:search/2`. 32 | 33 | ## [2.3.0] - 2019-07-10 34 | 35 | - New function `uef_format:format_bytes/1`. 36 | - New function `uef_format:format_bytes/2`. 37 | 38 | ## [2.2.0] - 2019-06-28 39 | 40 | - New function `uef_num:ctz/1`. 41 | - New function `uef_num:lsb_pos/1`. 42 | - New function `uef_num:msb_pos/1`. 43 | 44 | ## [2.1.0] - 2019-06-24 45 | 46 | - New function `uef_num:popcount/1`. 47 | 48 | ## [2.0.5] - 2019-05-31 49 | 50 | - Fixed: call of function `uef_format:format_price/3` could fail with exception when currency symbol or separator was an UTF-8 string (not binary). 51 | 52 | ## [2.0.4] - 2019-05-30 53 | 54 | - Fixed: `{get_warnings, true}` in rebar.config was the reason of high load when building PLT. 55 | - `xref` options added to rebar.config. 56 | - `cover` options added to rebar.config. 57 | 58 | ## [2.0.3] - 2019-05-30 59 | 60 | - Strict options added to rebar.config. 61 | - Fixed some typespecs and dialyzer warnings. 62 | 63 | ## [2.0.2] - 2019-05-29 64 | 65 | - Edoc tags added to all modules. 66 | 67 | ## [2.0.1] - 2019-05-22 68 | 69 | - Fixed: `uef.app.src` did not contain `uef_maps` module. 70 | 71 | ## [2.0.0] - 2019-05-19 72 | 73 | - OTP-19 or higher is now required. 74 | - New module `uef_maps`. 75 | - New function `uef_maps:delete_nested/2`. 76 | - New function `uef_maps:find_nested/2`. 77 | - New function `uef_maps:get_nested/2`. 78 | - New function `uef_maps:get_nested/3`. 79 | - New function `uef_maps:is_key_nested/2`. 80 | - New function `uef_maps:new_nested/1`. 81 | - New function `uef_maps:new_nested/2`. 82 | - New function `uef_maps:put_nested/3`. 83 | - New function `uef_maps:update_nested/3`. 84 | - New function `uef_maps:remove_nested/2`. 85 | - New function `uef_maps:take_nested/2`. 86 | 87 | ## [1.6.1] - 2019-05-12 88 | 89 | - Fixed: function `uef_time:unix_time/0` did not work with OTP-18 and OTP-19. 90 | - OTP-18 or higher is now required. 91 | 92 | ## [1.6.0] - 2019-05-12 93 | 94 | - New function `uef_time:unix_time/0`. 95 | - New function `uef_time:unix_time/1`. 96 | 97 | ## [1.5.0] - 2019-05-12 98 | 99 | - New function `uef_time:today/0`. 100 | - New function `uef_time:tomorrow/0`. 101 | - New function `uef_time:yesterday/0`. 102 | 103 | ## [1.4.0] - 2019-05-11 104 | 105 | - New functions `uef_time:add_seconds/1,2`. 106 | - New functions `uef_time:add_minutes/1,2`. 107 | - New functions `uef_time:add_hours/1,2`. 108 | - New functions `uef_time:add_days/1,2`. 109 | - New functions `uef_time:add_weeks/1,2`. 110 | - New functions `uef_time:add_months/1,2`. 111 | - New functions `uef_time:add_years/1,2`. 112 | - New functions `uef_time:add_time/1,2`. 113 | 114 | ## [1.3.0] - 2019-05-03 115 | 116 | - New function `uef_bin:repeat/2`. 117 | 118 | ## [1.2.0] - 2019-04-30 119 | 120 | - New function `uef_bin:reverse_utf8/1` which returns a binary in reverse character order. 121 | 122 | ## [1.1.0] - 2019-04-29 123 | 124 | - New function `uef_bin:reverse/1` which returns a binary in reverse byte order. 125 | 126 | ## [1.0.0] - 2019-04-29 127 | 128 | - New function `uef_format:format_number/3,4`. Supports custom precision, number of decimal digits, thousands separator, decimal point, currency symbols and custom currency position (left/right). 129 | - New function `uef_format:format_price/3` based on `uef_format:format_number/3`. 130 | - Module `uef_format` totally reworked. 131 | - Function `uef_format:format_price/1,2` now returns other value, see README. 132 | 133 | ## [0.1.0] - 2019-04-24 134 | 135 | - Initial release. 136 | -------------------------------------------------------------------------------- /Emakefile: -------------------------------------------------------------------------------- 1 | {["src/*"], [debug_info, {i,"include/"}, {outdir, "ebin/"}]}. 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Thanks to: https://github.com/chef/mini_s3 3 | # 4 | 5 | # 6 | # Use rebar3 from either: 7 | # - ./rebar3 8 | # - rebar3 on the PATH (found via which) 9 | # - Downloaded from $REBAR3_URL 10 | # 11 | 12 | .PHONY: all compile test dialyzer dialyze clean distclean shell docs edoc xref cover 13 | 14 | REBAR3_URL=https://s3.amazonaws.com/rebar3/rebar3 15 | 16 | ifeq ($(wildcard rebar3),rebar3) 17 | REBAR3 = $(CURDIR)/rebar3 18 | endif 19 | 20 | # Fallback to rebar on PATH 21 | REBAR3 ?= $(shell which rebar3) 22 | 23 | # And finally, prep to download rebar if all else fails 24 | ifeq ($(REBAR3),) 25 | REBAR3 = rebar3 26 | endif 27 | 28 | all: compile 29 | 30 | compile: $(REBAR3) 31 | @$(REBAR3) compile 32 | 33 | test: 34 | @$(REBAR3) eunit 35 | 36 | dialyzer: 37 | @$(REBAR3) dialyzer 38 | 39 | dialyze: dialyzer 40 | 41 | clean: 42 | @$(REBAR3) clean 43 | 44 | distclean: 45 | @rm -rf _build 46 | 47 | shell: 48 | @$(REBAR3) shell 49 | 50 | docs: 51 | @$(REBAR3) edoc 52 | 53 | edoc: docs 54 | 55 | xref: 56 | @$(REBAR3) xref 57 | 58 | cover: 59 | @$(REBAR3) do eunit, cover 60 | 61 | $(REBAR3): 62 | curl -Lo rebar3 $(REBAR3_URL) || wget $(REBAR3_URL) 63 | chmod a+x rebar3 64 | -------------------------------------------------------------------------------- /doc/overview.edoc: -------------------------------------------------------------------------------- 1 | ** this is the overview.doc file for the application 'uef' ** 2 | 3 | @author Sergei Semichev 4 | @copyright 2019-2024 Sergei Semichev 5 | @title The uef application 6 | @reference See uef-lib page on github for more information and examples. 7 | 8 | @doc uef-lib is a Useful Erlang Functions Library that provides modules for manipulating lists, binaries, maps, numbers, date and time. 9 | It can be used in OTP applications and contains some functions optimized for performance in specific cases (e.g. for file I/O operations or binary transformations). 10 | -------------------------------------------------------------------------------- /ebin/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /rebar.config: -------------------------------------------------------------------------------- 1 | {base_dir, "_build"}. 2 | {erl_opts, [debug_info, warn_missing_spec_all]}. 3 | 4 | {deps, []}. 5 | 6 | {minimum_otp_vsn, "19.0"}. 7 | 8 | {dialyzer, [ 9 | {get_warnings, false}, % DON'T set it to 'true'! This leads to a high load while building PLT 10 | {warnings, [ 11 | error_handling, underspecs, unmatched_returns, unknown 12 | ]} 13 | ]}. 14 | 15 | {xref_warnings,true}. 16 | {xref_checks, [ 17 | undefined_function_calls, undefined_functions, locals_not_used, 18 | deprecated_function_calls, deprecated_functions 19 | ]}. 20 | 21 | {project_plugins, [covertool]}. 22 | 23 | {cover_enabled, true}. 24 | {cover_export_enabled, true}. 25 | {cover_opts, [verbose]}. 26 | {covertool, [{coverdata_files, ["eunit.coverdata"]}]}. 27 | 28 | {profiles, [ 29 | {test, [ 30 | {dir, "test"}, 31 | {erl_opts, [nowarn_missing_spec_all]} 32 | ]} 33 | ]}. 34 | -------------------------------------------------------------------------------- /rebar.lock: -------------------------------------------------------------------------------- 1 | []. 2 | -------------------------------------------------------------------------------- /src/uef.app.src: -------------------------------------------------------------------------------- 1 | {application, uef, [ 2 | {description, "Useful Erlang Functions Library"}, 3 | {vsn, "2.6.0"}, 4 | {modules, [uef_bin, uef_crypt, uef_encode, uef_file, uef_format, uef_lists, uef_maps, uef_num, uef_time]}, 5 | {registered, []}, 6 | {applications, [kernel, stdlib]}, 7 | {env, []}, 8 | {licenses, ["Apache-2.0"]}, 9 | {links, [{"Github", "https://github.com/DOBRO/uef-lib"}]} 10 | ]}. 11 | -------------------------------------------------------------------------------- /src/uef_bin.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2019-2024, Sergei Semichev . All Rights Reserved. 2 | %% 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | 15 | -module(uef_bin). 16 | 17 | -export([binary_join/2, split/2, split/3]). 18 | -export([repeat/2]). 19 | -export([reverse/1, reverse_utf8/1]). 20 | -export([replace/3, replace_chars/3]). 21 | -export([random_latin_binary/2, random_binary_from_chars/2]). 22 | -export([numeric_prefix/1]). 23 | -export([strip_left/2, strip_right/2, strip_both/2]). 24 | -export([chomp/1]). 25 | 26 | -type split_option() :: undefined | trim_all. 27 | 28 | %%%------------------------------------------------------------------------------ 29 | %%% API 30 | %%%------------------------------------------------------------------------------ 31 | 32 | %% binary_join/2 33 | -spec binary_join(ListOfBinaries :: [binary()], Separator :: binary()) -> binary(). 34 | %% @doc 35 | %% Joins a list of binaries with separator into a single binary. Returns binary. 36 | %% @end 37 | binary_join([], _Sep) -> <<>>; 38 | binary_join([Bin], _Sep) -> Bin; 39 | binary_join([Head|Tail], Sep) -> 40 | lists:foldl(fun(Value, Acc) -> 41 | <> 42 | end, Head, Tail). 43 | 44 | %% split/2 45 | -spec split(Binary :: binary(), Splitter :: binary()) -> ListOfBinaries :: [binary()]. 46 | %% @doc 47 | %% Splits binary Binary with splitter Splitter into a list of binaries. 48 | %% Works as binary:split/2 but is more performant in simple cases. 49 | %% @end 50 | split(B, Splitter) -> 51 | split(B, Splitter, undefined). 52 | 53 | 54 | %% split/3 55 | -spec split(Binary :: binary(), Splitter :: binary(), SplitOption:: split_option()) -> ListOfBinaries :: [binary()]. 56 | %% @doc 57 | %% Splits binary Binary with splitter Splitter into a list of binaries. 58 | %% Works as uef_bin:split/2 but removes all epmty `(<<>>)' chunks. 59 | %% It can be used in simple cases instead of binary:split/3 for the reason that it's more performant. 60 | %% @end 61 | split(<<>>, _, _) -> []; 62 | split(B, <<>>, _) -> [B]; 63 | split(B, Splitter, Option) -> 64 | SplitterBitSize = erlang:bit_size(Splitter), 65 | List = do_split(B, Splitter, SplitterBitSize, []), 66 | case Option of 67 | trim_all -> lists:filter(fun(<<>>) -> false; (_) -> true end, List); 68 | _ -> List 69 | end. 70 | 71 | %% repeat/2 72 | -spec repeat(Binary1 :: binary(), N :: pos_integer()) -> Binary2 :: binary(). 73 | %% @doc 74 | %% Returns binary Binary2 consisting of Binary1 repeated N times. 75 | %% @end 76 | repeat(Bin, N) -> 77 | repeat(Bin, N, <<>>). 78 | 79 | 80 | %% reverse/1 81 | -spec reverse(binary()) -> binary(). 82 | %% @doc 83 | %% Returns a binary in reverse byte order. 84 | %% @end 85 | reverse(B) -> 86 | S = erlang:bit_size(B), 87 | <> = B, 88 | <>. 89 | 90 | %% reverse_utf8/1 91 | -spec reverse_utf8(UTF8_Binary1 :: binary()) -> UTF8_Binary2 :: binary(). 92 | %% @doc 93 | %% Returns a binary in reverse character order. Intended to work with UTF-8 binary strings. 94 | %% @end 95 | reverse_utf8(Bin) -> 96 | reverse_utf8(Bin, <<>>). 97 | 98 | %% replace/3 99 | -spec replace(Binary1 :: binary(), Chars :: binary(), OtherChars :: binary()) -> Binary2 :: binary(). 100 | %% @doc 101 | %% Replaces chars Chars with other chars OtherChars in binary Binary1 and returns another binary Binary2. 102 | %% Works as binary:replace/3 but more permormant and can be used in simple cases. 103 | %% @end 104 | replace(<<>>, _, _) -> <<>>; 105 | replace(B, <<>>, _) -> B; 106 | replace(B, C1, C2) -> 107 | C1BitSize = erlang:bit_size(C1), 108 | replace(B, C1, C1BitSize, C2, <<>>). 109 | 110 | 111 | %% replace_chars/3 112 | -spec replace_chars(Binary1 :: binary(), ListOfCharsToReplace :: [binary()], OtherChars :: binary()) -> Binary2 :: binary(). 113 | %% @doc 114 | %% Replaces chars inluded in list ListOfCharsToReplace with other chars OtherChars in binary Binary1 and returns another binary Binary2. 115 | %% @end 116 | replace_chars(B0, [], _) -> B0; 117 | replace_chars(B0, Chars, ToChar) -> 118 | lists:foldl(fun(Ch, B) -> 119 | replace(B, Ch, ToChar) 120 | end, B0, Chars). 121 | 122 | 123 | %% random_latin_binary/2 124 | -spec random_latin_binary(Length :: pos_integer(), CaseFlag :: lower | upper | any) -> binary(). 125 | %% @doc 126 | %% Returns a random binary of size Length consisting of latins [a-zA-Z] and digits [0-9]. 127 | %% The second argument CaseFlag corresponds to a letter case, an atom 'lower', 'upper' or 'any'. 128 | %% @end 129 | random_latin_binary(Length, CaseFlag) -> 130 | Chars = case CaseFlag of 131 | lower -> <<"abcdefghijklmnopqrstuvwxyz0123456789">>; 132 | upper -> <<"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789">>; 133 | any -> <<"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789">> 134 | end, 135 | random_binary_from_chars(Length, Chars). 136 | 137 | 138 | %% random_binary_from_chars/2 139 | -spec random_binary_from_chars(Length :: pos_integer(), Chars :: binary()) -> binary(). 140 | %% @doc 141 | %% Generates and returns a binary of size Length which consists of the given characters Chars. 142 | %% @end 143 | random_binary_from_chars(Length, Chars) -> 144 | Bsize = erlang:byte_size(Chars), 145 | lists:foldl( 146 | fun(_, Acc) -> 147 | RndChar = binary:at(Chars, rand:uniform(Bsize)-1), 148 | << Acc/binary, RndChar >> 149 | end, 150 | <<>>, 151 | lists:seq(1, Length) 152 | ). 153 | 154 | 155 | %% numeric_prefix/1 156 | -spec numeric_prefix(Binary :: binary()) -> DigitsOnlyOrEmptyBinary :: binary(). 157 | %% @doc 158 | %% Returns new binary DigitsOnlyBinary which consists of digits [0-9] wich are at the beginning in the given binary Binary. 159 | %% If Binary does not begin with digit, this function returns empty binary `(<<>>)'. 160 | %% @end 161 | numeric_prefix(B) -> numeric_prefix(B, <<>>). 162 | 163 | 164 | %% strip_left/2 165 | -spec strip_left(Bin :: binary(), Chars :: binary() | integer()) -> binary(). 166 | %% @doc 167 | %% Removes leading Chars from binary Bin and returns new binary. 168 | %% @end 169 | strip_left(Bin, <<>>) when is_binary(Bin) -> 170 | Bin; 171 | strip_left(Bin, Chars) when is_binary(Bin), is_binary(Chars) -> 172 | do_strip_left(Bin, Chars, erlang:byte_size(Chars)); 173 | strip_left(Bin, Chars) when is_binary(Bin), is_integer(Chars) -> 174 | strip_left(Bin, << Chars >>). 175 | 176 | %% strip_right/2 177 | -spec strip_right(Bin :: binary(), Chars :: binary() | integer()) -> binary(). 178 | %% @doc 179 | %% Removes trailing Chars from binary Bin and returns new binary. 180 | %% @end 181 | strip_right(Bin, <<>>) when is_binary(Bin) -> 182 | Bin; 183 | strip_right(Bin, Chars) when is_binary(Bin), is_binary(Chars) -> 184 | do_strip_right(Bin, Chars, erlang:byte_size(Chars)); 185 | strip_right(Bin, Chars) when is_binary(Bin), is_integer(Chars) -> 186 | strip_right(Bin, << Chars >>). 187 | 188 | 189 | %% strip_both/2 190 | -spec strip_both(Bin :: binary(), Chars :: binary() | integer()) -> binary(). 191 | %% @doc 192 | %% Removes leading and trailing Chars from binary Bin and returns new binary. 193 | %% @end 194 | strip_both(Bin, Chars) -> 195 | strip_right(strip_left(Bin, Chars), Chars). 196 | 197 | 198 | %% chomp/1 199 | -spec chomp(binary()) -> binary(). 200 | %% @doc 201 | %% Removes all trailing \n and \r characters from binary. 202 | %% @end 203 | chomp(<<>>) -> 204 | <<>>; 205 | chomp(Bin) -> 206 | HeadSize = erlang:byte_size(Bin) - 1, 207 | case Bin of 208 | << Head:HeadSize/bytes, C >> when C =:= $\n orelse C =:= $\r -> 209 | chomp(Head); 210 | _ -> 211 | Bin 212 | end. 213 | 214 | %%%------------------------------------------------------------------------------ 215 | %%% Internal functions 216 | %%%------------------------------------------------------------------------------ 217 | 218 | %% repeat/3 219 | -spec repeat(binary(), pos_integer(), binary()) -> binary(). 220 | repeat(_, N, Acc) when N < 1 -> 221 | Acc; 222 | repeat(Bin, N, Acc) -> 223 | repeat(Bin, N-1, <>). 224 | 225 | 226 | %% replace/4 227 | -spec replace(binary(), binary(), pos_integer(), binary(), binary()) -> binary(). 228 | replace(B, C1, C1BitSize, C2, Acc) -> 229 | case B of 230 | <<>> -> 231 | Acc; 232 | <> -> 233 | replace(Rest, C1, C1BitSize, C2, <>); % replacement 234 | <> -> 235 | replace(Rest, C1, C1BitSize, C2, <>) 236 | end. 237 | 238 | 239 | %% reverse_utf8/2 240 | -spec reverse_utf8(binary(), binary()) -> binary(). 241 | reverse_utf8(<<>>, Acc) -> Acc; 242 | reverse_utf8(<>, Acc) -> 243 | reverse_utf8(Rest, <>). 244 | 245 | 246 | %% do_split/3 247 | -spec do_split(binary(), binary(), pos_integer(), [binary()]) -> [binary()]. 248 | do_split(B, Splitter, SplitterBitSize, List) -> 249 | case B of 250 | <<>> -> 251 | lists:reverse(List); 252 | <> -> 253 | case List of 254 | [_|_] -> do_split(Rest, Splitter, SplitterBitSize, [<<>> | List]); 255 | [] -> do_split(Rest, Splitter, SplitterBitSize, [<<>>, <<>> | List]) 256 | end; 257 | <> -> 258 | List2 = case List of 259 | [H|T] -> [<> | T]; 260 | [] -> [<< C >>] 261 | end, 262 | do_split(Rest, Splitter, SplitterBitSize, List2) 263 | end. 264 | 265 | 266 | %% numeric_prefix/2 267 | -spec numeric_prefix(binary(), binary()) -> binary(). 268 | numeric_prefix(<< $0, Rest/bits >>, Acc) -> numeric_prefix(Rest, << Acc/bits, $0 >>); 269 | numeric_prefix(<< $1, Rest/bits >>, Acc) -> numeric_prefix(Rest, << Acc/bits, $1 >>); 270 | numeric_prefix(<< $2, Rest/bits >>, Acc) -> numeric_prefix(Rest, << Acc/bits, $2 >>); 271 | numeric_prefix(<< $3, Rest/bits >>, Acc) -> numeric_prefix(Rest, << Acc/bits, $3 >>); 272 | numeric_prefix(<< $4, Rest/bits >>, Acc) -> numeric_prefix(Rest, << Acc/bits, $4 >>); 273 | numeric_prefix(<< $5, Rest/bits >>, Acc) -> numeric_prefix(Rest, << Acc/bits, $5 >>); 274 | numeric_prefix(<< $6, Rest/bits >>, Acc) -> numeric_prefix(Rest, << Acc/bits, $6 >>); 275 | numeric_prefix(<< $7, Rest/bits >>, Acc) -> numeric_prefix(Rest, << Acc/bits, $7 >>); 276 | numeric_prefix(<< $8, Rest/bits >>, Acc) -> numeric_prefix(Rest, << Acc/bits, $8 >>); 277 | numeric_prefix(<< $9, Rest/bits >>, Acc) -> numeric_prefix(Rest, << Acc/bits, $9 >>); 278 | numeric_prefix(_, Acc) -> Acc. 279 | 280 | 281 | %% do_strip_left/3 282 | -spec do_strip_left(binary(), binary(), pos_integer()) -> binary(). 283 | do_strip_left(<<>>, _, _) -> 284 | <<>>; 285 | do_strip_left(Bin, Chars, CharsByteSize) -> 286 | case Bin of 287 | << Chars:CharsByteSize/bytes, Rest/bits >> -> 288 | do_strip_left(Rest, Chars, CharsByteSize); 289 | _ -> Bin 290 | end. 291 | 292 | 293 | %% do_strip_right/3 294 | -spec do_strip_right(binary(), binary(), pos_integer()) -> binary(). 295 | do_strip_right(<<>>, _, _) -> 296 | <<>>; 297 | do_strip_right(Bin, Chars, CharsByteSize) -> 298 | HeadByteSize = erlang:byte_size(Bin) - CharsByteSize, 299 | case Bin of 300 | << Head:HeadByteSize/bytes, Chars/bits >> -> 301 | do_strip_right(Head, Chars, CharsByteSize); 302 | _ -> Bin 303 | end. 304 | -------------------------------------------------------------------------------- /src/uef_crypt.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2019-2024, Sergei Semichev . All Rights Reserved. 2 | %% 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | 15 | -module(uef_crypt). 16 | 17 | -export([md5_hex/1]). 18 | 19 | %%%------------------------------------------------------------------------------ 20 | %%% API 21 | %%%------------------------------------------------------------------------------ 22 | 23 | %% md5_hex/1 24 | -spec md5_hex(IoData :: iodata()) -> Binary :: binary(). 25 | %% @doc 26 | %% Returns binary Binary in hexadecimal form of md5 hash of the argument IoData 27 | %% @end 28 | md5_hex(IoData) -> 29 | hstr(erlang:md5(IoData)). 30 | 31 | 32 | %%%------------------------------------------------------------------------------ 33 | %%% Internal functions 34 | %%%------------------------------------------------------------------------------ 35 | 36 | %% hstr/1 37 | -spec hstr(binary()) -> binary(). 38 | hstr(B) when is_binary(B) -> 39 | T = {$0,$1,$2,$3,$4,$5,$6,$7,$8,$9,$a,$b,$c,$d,$e,$f}, 40 | << <<(element(X bsr 4 + 1, T)), (element(X band 16#0F + 1, T))>> 41 | || <> <= B >>. 42 | -------------------------------------------------------------------------------- /src/uef_encode.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2019-2024, Sergei Semichev . All Rights Reserved. 2 | %% 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | 15 | -module(uef_encode). 16 | 17 | -export([html_encode_list/1, html_encode_bin/1]). 18 | -export([win_to_utf8/1]). 19 | 20 | %%%------------------------------------------------------------------------------ 21 | %%% API 22 | %%%------------------------------------------------------------------------------ 23 | 24 | %% html_encode_list/1 25 | -spec html_encode_list(Html::iodata()) -> EncodedList :: [binary()]. 26 | %% @doc 27 | %% Takes argument Html, replaces some unsafe symbols with their appropriate HTML entities and returns list of binaries. 28 | %% @end 29 | html_encode_list(<<>>) -> []; 30 | html_encode_list([]) -> []; 31 | html_encode_list(Html) -> 32 | case unicode:characters_to_list(Html) of 33 | {incomplete, Encoded, _} -> 34 | html_encode_list(Encoded, []); 35 | {error, Encoded, _} -> 36 | html_encode_list(Encoded, []); 37 | List -> 38 | html_encode_list(List, []) 39 | end. 40 | 41 | %% html_encode_bin/1 42 | -spec html_encode_bin(Html::iodata()) -> EncodedBinary :: binary(). 43 | %% @doc 44 | %% Takes argument Html, replaces some unsafe symbols with their appropriate HTML entities and returns binary. 45 | %% @end 46 | html_encode_bin(<<>>) -> <<>>; 47 | html_encode_bin([]) -> <<>>; 48 | html_encode_bin(Html) -> unicode:characters_to_binary(html_encode_list(Html)). 49 | 50 | %% win_to_utf8/1 51 | -spec win_to_utf8(Binary1251 :: binary()) -> BinaryUtf8 :: binary(). 52 | %% @doc 53 | %% Converts cp1251 binary to utf-8 binary. 54 | %% @end 55 | win_to_utf8(Bin) -> 56 | win_to_utf8(Bin, <<>>). 57 | 58 | 59 | %%%------------------------------------------------------------------------------ 60 | %%% Internal functions 61 | %%%------------------------------------------------------------------------------ 62 | 63 | %% html_encode/2 64 | -spec html_encode_list(iodata(), [binary()]) -> [binary()]. 65 | html_encode_list([], Acc) -> lists:reverse(Acc); 66 | html_encode_list([H|T], Acc) -> html_encode_list(T, [html_encode_char(H)|Acc]). 67 | 68 | %% html_encode_char/1 69 | -spec html_encode_char(pos_integer()) -> binary(). 70 | html_encode_char($\n) -> <<"
">>; 71 | html_encode_char($\r) -> <<>>; 72 | html_encode_char($\t) -> <<" ">>; 73 | html_encode_char($") -> <<""">>; 74 | html_encode_char($—) -> <<"—">>; 75 | html_encode_char($|) -> <<"|">>; 76 | html_encode_char($/) -> <<"/">>; 77 | html_encode_char($[) -> <<"[">>; 78 | html_encode_char($]) -> <<"]">>; 79 | html_encode_char(${) -> <<"{">>; 80 | html_encode_char($}) -> <<"}">>; 81 | html_encode_char($&) -> <<"&">>; 82 | html_encode_char($<) -> <<"<">>; 83 | html_encode_char($>) -> <<">">>; 84 | html_encode_char($') -> <<"'">>; 85 | html_encode_char($%) -> <<"%">>; 86 | html_encode_char($#) -> <<"#">>; 87 | html_encode_char($№) -> <<"№">>; 88 | html_encode_char($@) -> <<"@">>; 89 | html_encode_char($~) -> <<"~">>; 90 | html_encode_char($•) -> <<"•">>; 91 | html_encode_char($○) -> <<"○">>; 92 | html_encode_char($●) -> <<"●">>; 93 | html_encode_char($™) -> <<"™">>; 94 | html_encode_char($©) -> <<"©">>; 95 | html_encode_char($®) -> <<"®">>; 96 | html_encode_char($¶) -> <<"¶">>; 97 | html_encode_char($♢) -> <<"♢">>; 98 | html_encode_char($♦) -> <<"♦">>; 99 | html_encode_char($$) -> <<"$">>; 100 | html_encode_char($€) -> <<"€">>; 101 | html_encode_char($«) -> <<"«">>; 102 | html_encode_char($») -> <<"»">>; 103 | html_encode_char($„) -> <<"„">>; 104 | html_encode_char($‚) -> <<"‚">>; 105 | html_encode_char($`) -> <<"`">>; 106 | html_encode_char($°) -> <<"°">>; 107 | html_encode_char($±) -> <<"±">>; 108 | html_encode_char($´) -> <<"´">>; 109 | html_encode_char($‘) -> <<"‘">>; 110 | html_encode_char($’) -> <<"’">>; 111 | html_encode_char($“) -> <<"“">>; 112 | html_encode_char($”) -> <<"”">>; 113 | html_encode_char($‹) -> <<"‹">>; 114 | html_encode_char($›) -> <<"›">>; 115 | html_encode_char($→) -> <<"→">>; 116 | html_encode_char($←) -> <<"←">>; 117 | html_encode_char($↑) -> <<"↑">>; 118 | html_encode_char($↓) -> <<"↓">>; 119 | html_encode_char($↔) -> <<"↔">>; 120 | html_encode_char($…) -> <<"…">>; 121 | html_encode_char($§) -> <<"§">>; 122 | html_encode_char($µ) -> <<"µ">>; 123 | html_encode_char($·) -> <<"·">>; 124 | html_encode_char($º) -> <<"º">>; 125 | html_encode_char($÷) -> <<"÷">>; 126 | html_encode_char($ο) -> <<"ο">>; 127 | html_encode_char($Ο) -> <<"Ο">>; 128 | html_encode_char($σ) -> <<"σ">>; 129 | html_encode_char($Σ) -> <<"Σ">>; 130 | html_encode_char($ω) -> <<"ω">>; 131 | html_encode_char($Ω) -> <<"Ω">>; 132 | html_encode_char($¬) -> <<"¬">>; 133 | html_encode_char($¦) -> <<"¦">>; 134 | html_encode_char($∞) -> <<"∞">>; 135 | html_encode_char($¹) -> <<"¹">>; 136 | html_encode_char($²) -> <<"²">>; 137 | html_encode_char($³) -> <<"³">>; 138 | html_encode_char($½) -> <<"½">>; 139 | html_encode_char($⅓) -> <<"⅓">>; 140 | html_encode_char($¼) -> <<"¼">>; 141 | html_encode_char(C) -> C. 142 | 143 | %% win_to_utf8/2 144 | -spec win_to_utf8(binary(), binary()) -> binary(). 145 | win_to_utf8(<>, Acc) -> 146 | U = case C of 147 | 16#00 -> 16#0000; %% NULL 148 | 16#01 -> 16#0001; %% START OF HEADING 149 | 16#02 -> 16#0002; %% START OF TEXT 150 | 16#03 -> 16#0003; %% END OF TEXT 151 | 16#04 -> 16#0004; %% END OF TRANSMISSION 152 | 16#05 -> 16#0005; %% ENQUIRY 153 | 16#06 -> 16#0006; %% ACKNOWLEDGE 154 | 16#07 -> 16#0007; %% BELL 155 | 16#08 -> 16#0008; %% BACKSPACE 156 | 16#09 -> 16#0009; %% HORIZONTAL TABULATION 157 | 16#0A -> 16#000A; %% LINE FEED 158 | 16#0B -> 16#000B; %% VERTICAL TABULATION 159 | 16#0C -> 16#000C; %% FORM FEED 160 | 16#0D -> 16#000D; %% CARRIAGE RETURN 161 | 16#0E -> 16#000E; %% SHIFT OUT 162 | 16#0F -> 16#000F; %% SHIFT IN 163 | 16#10 -> 16#0010; %% DATA LINK ESCAPE 164 | 16#11 -> 16#0011; %% DEVICE CONTROL ONE 165 | 16#12 -> 16#0012; %% DEVICE CONTROL TWO 166 | 16#13 -> 16#0013; %% DEVICE CONTROL THREE 167 | 16#14 -> 16#0014; %% DEVICE CONTROL FOUR 168 | 16#15 -> 16#0015; %% NEGATIVE ACKNOWLEDGE 169 | 16#16 -> 16#0016; %% SYNCHRONOUS IDLE 170 | 16#17 -> 16#0017; %% END OF TRANSMISSION BLOCK 171 | 16#18 -> 16#0018; %% CANCEL 172 | 16#19 -> 16#0019; %% END OF MEDIUM 173 | 16#1A -> 16#001A; %% SUBSTITUTE 174 | 16#1B -> 16#001B; %% ESCAPE 175 | 16#1C -> 16#001C; %% FILE SEPARATOR 176 | 16#1D -> 16#001D; %% GROUP SEPARATOR 177 | 16#1E -> 16#001E; %% RECORD SEPARATOR 178 | 16#1F -> 16#001F; %% UNIT SEPARATOR 179 | 16#20 -> 16#0020; %% SPACE 180 | 16#21 -> 16#0021; %% EXCLAMATION MARK 181 | 16#22 -> 16#0022; %% QUOTATION MARK 182 | 16#23 -> 16#0023; %% NUMBER SIGN 183 | 16#24 -> 16#0024; %% DOLLAR SIGN 184 | 16#25 -> 16#0025; %% PERCENT SIGN 185 | 16#26 -> 16#0026; %% AMPERSAND 186 | 16#27 -> 16#0027; %% APOSTROPHE 187 | 16#28 -> 16#0028; %% LEFT PARENTHESIS 188 | 16#29 -> 16#0029; %% RIGHT PARENTHESIS 189 | 16#2A -> 16#002A; %% ASTERISK 190 | 16#2B -> 16#002B; %% PLUS SIGN 191 | 16#2C -> 16#002C; %% COMMA 192 | 16#2D -> 16#002D; %% HYPHEN-MINUS 193 | 16#2E -> 16#002E; %% FULL STOP 194 | 16#2F -> 16#002F; %% SOLIDUS 195 | 16#30 -> 16#0030; %% DIGIT ZERO 196 | 16#31 -> 16#0031; %% DIGIT ONE 197 | 16#32 -> 16#0032; %% DIGIT TWO 198 | 16#33 -> 16#0033; %% DIGIT THREE 199 | 16#34 -> 16#0034; %% DIGIT FOUR 200 | 16#35 -> 16#0035; %% DIGIT FIVE 201 | 16#36 -> 16#0036; %% DIGIT SIX 202 | 16#37 -> 16#0037; %% DIGIT SEVEN 203 | 16#38 -> 16#0038; %% DIGIT EIGHT 204 | 16#39 -> 16#0039; %% DIGIT NINE 205 | 16#3A -> 16#003A; %% COLON 206 | 16#3B -> 16#003B; %% SEMICOLON 207 | 16#3C -> 16#003C; %% LESS-THAN SIGN 208 | 16#3D -> 16#003D; %% EQUALS SIGN 209 | 16#3E -> 16#003E; %% GREATER-THAN SIGN 210 | 16#3F -> 16#003F; %% QUESTION MARK 211 | 16#40 -> 16#0040; %% COMMERCIAL AT 212 | 16#41 -> 16#0041; %% LATIN CAPITAL LETTER A 213 | 16#42 -> 16#0042; %% LATIN CAPITAL LETTER B 214 | 16#43 -> 16#0043; %% LATIN CAPITAL LETTER C 215 | 16#44 -> 16#0044; %% LATIN CAPITAL LETTER D 216 | 16#45 -> 16#0045; %% LATIN CAPITAL LETTER E 217 | 16#46 -> 16#0046; %% LATIN CAPITAL LETTER F 218 | 16#47 -> 16#0047; %% LATIN CAPITAL LETTER G 219 | 16#48 -> 16#0048; %% LATIN CAPITAL LETTER H 220 | 16#49 -> 16#0049; %% LATIN CAPITAL LETTER I 221 | 16#4A -> 16#004A; %% LATIN CAPITAL LETTER J 222 | 16#4B -> 16#004B; %% LATIN CAPITAL LETTER K 223 | 16#4C -> 16#004C; %% LATIN CAPITAL LETTER L 224 | 16#4D -> 16#004D; %% LATIN CAPITAL LETTER M 225 | 16#4E -> 16#004E; %% LATIN CAPITAL LETTER N 226 | 16#4F -> 16#004F; %% LATIN CAPITAL LETTER O 227 | 16#50 -> 16#0050; %% LATIN CAPITAL LETTER P 228 | 16#51 -> 16#0051; %% LATIN CAPITAL LETTER Q 229 | 16#52 -> 16#0052; %% LATIN CAPITAL LETTER R 230 | 16#53 -> 16#0053; %% LATIN CAPITAL LETTER S 231 | 16#54 -> 16#0054; %% LATIN CAPITAL LETTER T 232 | 16#55 -> 16#0055; %% LATIN CAPITAL LETTER U 233 | 16#56 -> 16#0056; %% LATIN CAPITAL LETTER V 234 | 16#57 -> 16#0057; %% LATIN CAPITAL LETTER W 235 | 16#58 -> 16#0058; %% LATIN CAPITAL LETTER X 236 | 16#59 -> 16#0059; %% LATIN CAPITAL LETTER Y 237 | 16#5A -> 16#005A; %% LATIN CAPITAL LETTER Z 238 | 16#5B -> 16#005B; %% LEFT SQUARE BRACKET 239 | 16#5C -> 16#005C; %% REVERSE SOLIDUS 240 | 16#5D -> 16#005D; %% RIGHT SQUARE BRACKET 241 | 16#5E -> 16#005E; %% CIRCUMFLEX ACCENT 242 | 16#5F -> 16#005F; %% LOW LINE 243 | 16#60 -> 16#0060; %% GRAVE ACCENT 244 | 16#61 -> 16#0061; %% LATIN SMALL LETTER A 245 | 16#62 -> 16#0062; %% LATIN SMALL LETTER B 246 | 16#63 -> 16#0063; %% LATIN SMALL LETTER C 247 | 16#64 -> 16#0064; %% LATIN SMALL LETTER D 248 | 16#65 -> 16#0065; %% LATIN SMALL LETTER E 249 | 16#66 -> 16#0066; %% LATIN SMALL LETTER F 250 | 16#67 -> 16#0067; %% LATIN SMALL LETTER G 251 | 16#68 -> 16#0068; %% LATIN SMALL LETTER H 252 | 16#69 -> 16#0069; %% LATIN SMALL LETTER I 253 | 16#6A -> 16#006A; %% LATIN SMALL LETTER J 254 | 16#6B -> 16#006B; %% LATIN SMALL LETTER K 255 | 16#6C -> 16#006C; %% LATIN SMALL LETTER L 256 | 16#6D -> 16#006D; %% LATIN SMALL LETTER M 257 | 16#6E -> 16#006E; %% LATIN SMALL LETTER N 258 | 16#6F -> 16#006F; %% LATIN SMALL LETTER O 259 | 16#70 -> 16#0070; %% LATIN SMALL LETTER P 260 | 16#71 -> 16#0071; %% LATIN SMALL LETTER Q 261 | 16#72 -> 16#0072; %% LATIN SMALL LETTER R 262 | 16#73 -> 16#0073; %% LATIN SMALL LETTER S 263 | 16#74 -> 16#0074; %% LATIN SMALL LETTER T 264 | 16#75 -> 16#0075; %% LATIN SMALL LETTER U 265 | 16#76 -> 16#0076; %% LATIN SMALL LETTER V 266 | 16#77 -> 16#0077; %% LATIN SMALL LETTER W 267 | 16#78 -> 16#0078; %% LATIN SMALL LETTER X 268 | 16#79 -> 16#0079; %% LATIN SMALL LETTER Y 269 | 16#7A -> 16#007A; %% LATIN SMALL LETTER Z 270 | 16#7B -> 16#007B; %% LEFT CURLY BRACKET 271 | 16#7C -> 16#007C; %% VERTICAL LINE 272 | 16#7D -> 16#007D; %% RIGHT CURLY BRACKET 273 | 16#7E -> 16#007E; %% TILDE 274 | 16#7F -> 16#007F; %% DELETE 275 | 16#80 -> 16#0402; %% CYRILLIC CAPITAL LETTER DJE 276 | 16#81 -> 16#0403; %% CYRILLIC CAPITAL LETTER GJE 277 | 16#82 -> 16#201A; %% SINGLE LOW-9 QUOTATION MARK 278 | 16#83 -> 16#0453; %% CYRILLIC SMALL LETTER GJE 279 | 16#84 -> 16#201E; %% DOUBLE LOW-9 QUOTATION MARK 280 | 16#85 -> 16#2026; %% HORIZONTAL ELLIPSIS 281 | 16#86 -> 16#2020; %% DAGGER 282 | 16#87 -> 16#2021; %% DOUBLE DAGGER 283 | 16#88 -> 16#20AC; %% EURO SIGN 284 | 16#89 -> 16#2030; %% PER MILLE SIGN 285 | 16#8A -> 16#0409; %% CYRILLIC CAPITAL LETTER LJE 286 | 16#8B -> 16#2039; %% SINGLE LEFT-POINTING ANGLE QUOTATION MARK 287 | 16#8C -> 16#040A; %% CYRILLIC CAPITAL LETTER NJE 288 | 16#8D -> 16#040C; %% CYRILLIC CAPITAL LETTER KJE 289 | 16#8E -> 16#040B; %% CYRILLIC CAPITAL LETTER TSHE 290 | 16#8F -> 16#040F; %% CYRILLIC CAPITAL LETTER DZHE 291 | 16#90 -> 16#0452; %% CYRILLIC SMALL LETTER DJE 292 | 16#91 -> 16#2018; %% LEFT SINGLE QUOTATION MARK 293 | 16#92 -> 16#2019; %% RIGHT SINGLE QUOTATION MARK 294 | 16#93 -> 16#201C; %% LEFT DOUBLE QUOTATION MARK 295 | 16#94 -> 16#201D; %% RIGHT DOUBLE QUOTATION MARK 296 | 16#95 -> 16#2024; %% BULLET 297 | 16#96 -> 16#2013; %% EN DASH 298 | 16#97 -> 16#2014; %% EM DASH 299 | 16#99 -> 16#2122; %% TRADE MARK SIGN 300 | 16#9A -> 16#0459; %% CYRILLIC SMALL LETTER LJE 301 | 16#9B -> 16#203A; %% SINGLE RIGHT-POINTING ANGLE QUOTATION MARK 302 | 16#9C -> 16#045A; %% CYRILLIC SMALL LETTER NJE 303 | 16#9D -> 16#045C; %% CYRILLIC SMALL LETTER KJE 304 | 16#9E -> 16#045B; %% CYRILLIC SMALL LETTER TSHE 305 | 16#9F -> 16#045F; %% CYRILLIC SMALL LETTER DZHE 306 | 16#A0 -> 16#00A0; %% NO-BREAK SPACE 307 | 16#A1 -> 16#040E; %% CYRILLIC CAPITAL LETTER SHORT U 308 | 16#A2 -> 16#045E; %% CYRILLIC SMALL LETTER SHORT U 309 | 16#A3 -> 16#0408; %% CYRILLIC CAPITAL LETTER JE 310 | 16#A4 -> 16#00A4; %% CURRENCY SIGN 311 | 16#A5 -> 16#0490; %% CYRILLIC CAPITAL LETTER GHE WITH UPTURN 312 | 16#A6 -> 16#00A6; %% BROKEN BAR 313 | 16#A7 -> 16#00A7; %% SECTION SIGN 314 | 16#A8 -> 16#0401; %% CYRILLIC CAPITAL LETTER IO 315 | 16#A9 -> 16#00A9; %% COPYRIGHT SIGN 316 | 16#AA -> 16#0404; %% CYRILLIC CAPITAL LETTER UKRAINIAN IE 317 | 16#AB -> 16#00AB; %% LEFT-POINTING DOUBLE ANGLE QUOTATION MARK 318 | 16#AC -> 16#00AC; %% NOT SIGN 319 | 16#AD -> 16#00AD; %% SOFT HYPHEN 320 | 16#AE -> 16#00AE; %% REGISTERED SIGN 321 | 16#AF -> 16#0407; %% CYRILLIC CAPITAL LETTER YI 322 | 16#B0 -> 16#00B0; %% DEGREE SIGN 323 | 16#B1 -> 16#00B1; %% PLUS-MINUS SIGN 324 | 16#B2 -> 16#0406; %% CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I 325 | 16#B3 -> 16#0456; %% CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I 326 | 16#B4 -> 16#0491; %% CYRILLIC SMALL LETTER GHE WITH UPTURN 327 | 16#B5 -> 16#00B5; %% MICRO SIGN 328 | 16#B6 -> 16#00B6; %% PILCROW SIGN 329 | 16#B7 -> 16#00B7; %% MIDDLE DOT 330 | 16#B8 -> 16#0451; %% CYRILLIC SMALL LETTER IO 331 | 16#B9 -> 16#2116; %% NUMERO SIGN 332 | 16#BA -> 16#0454; %% CYRILLIC SMALL LETTER UKRAINIAN IE 333 | 16#BB -> 16#00BB; %% RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK 334 | 16#BC -> 16#0458; %% CYRILLIC SMALL LETTER JE 335 | 16#BD -> 16#0405; %% CYRILLIC CAPITAL LETTER DZE 336 | 16#BE -> 16#0455; %% CYRILLIC SMALL LETTER DZE 337 | 16#BF -> 16#0457; %% CYRILLIC SMALL LETTER YI 338 | 16#C0 -> 16#0410; %% CYRILLIC CAPITAL LETTER A 339 | 16#C1 -> 16#0411; %% CYRILLIC CAPITAL LETTER BE 340 | 16#C2 -> 16#0412; %% CYRILLIC CAPITAL LETTER VE 341 | 16#C3 -> 16#0413; %% CYRILLIC CAPITAL LETTER GHE 342 | 16#C4 -> 16#0414; %% CYRILLIC CAPITAL LETTER DE 343 | 16#C5 -> 16#0415; %% CYRILLIC CAPITAL LETTER IE 344 | 16#C6 -> 16#0416; %% CYRILLIC CAPITAL LETTER ZHE 345 | 16#C7 -> 16#0417; %% CYRILLIC CAPITAL LETTER ZE 346 | 16#C8 -> 16#0418; %% CYRILLIC CAPITAL LETTER I 347 | 16#C9 -> 16#0419; %% CYRILLIC CAPITAL LETTER SHORT I 348 | 16#CA -> 16#041A; %% CYRILLIC CAPITAL LETTER KA 349 | 16#CB -> 16#041B; %% CYRILLIC CAPITAL LETTER EL 350 | 16#CC -> 16#041C; %% CYRILLIC CAPITAL LETTER EM 351 | 16#CD -> 16#041D; %% CYRILLIC CAPITAL LETTER EN 352 | 16#CE -> 16#041E; %% CYRILLIC CAPITAL LETTER O 353 | 16#CF -> 16#041F; %% CYRILLIC CAPITAL LETTER PE 354 | 16#D0 -> 16#0420; %% CYRILLIC CAPITAL LETTER ER 355 | 16#D1 -> 16#0421; %% CYRILLIC CAPITAL LETTER ES 356 | 16#D2 -> 16#0422; %% CYRILLIC CAPITAL LETTER TE 357 | 16#D3 -> 16#0423; %% CYRILLIC CAPITAL LETTER U 358 | 16#D4 -> 16#0424; %% CYRILLIC CAPITAL LETTER EF 359 | 16#D5 -> 16#0425; %% CYRILLIC CAPITAL LETTER HA 360 | 16#D6 -> 16#0426; %% CYRILLIC CAPITAL LETTER TSE 361 | 16#D7 -> 16#0427; %% CYRILLIC CAPITAL LETTER CHE 362 | 16#D8 -> 16#0428; %% CYRILLIC CAPITAL LETTER SHA 363 | 16#D9 -> 16#0429; %% CYRILLIC CAPITAL LETTER SHCHA 364 | 16#DA -> 16#042A; %% CYRILLIC CAPITAL LETTER HARD SIGN 365 | 16#DB -> 16#042B; %% CYRILLIC CAPITAL LETTER YERU 366 | 16#DC -> 16#042C; %% CYRILLIC CAPITAL LETTER SOFT SIGN 367 | 16#DD -> 16#042D; %% CYRILLIC CAPITAL LETTER E 368 | 16#DE -> 16#042E; %% CYRILLIC CAPITAL LETTER YU 369 | 16#DF -> 16#042F; %% CYRILLIC CAPITAL LETTER YA 370 | 16#E0 -> 16#0430; %% CYRILLIC SMALL LETTER A 371 | 16#E1 -> 16#0431; %% CYRILLIC SMALL LETTER BE 372 | 16#E2 -> 16#0432; %% CYRILLIC SMALL LETTER VE 373 | 16#E3 -> 16#0433; %% CYRILLIC SMALL LETTER GHE 374 | 16#E4 -> 16#0434; %% CYRILLIC SMALL LETTER DE 375 | 16#E5 -> 16#0435; %% CYRILLIC SMALL LETTER IE 376 | 16#E6 -> 16#0436; %% CYRILLIC SMALL LETTER ZHE 377 | 16#E7 -> 16#0437; %% CYRILLIC SMALL LETTER ZE 378 | 16#E8 -> 16#0438; %% CYRILLIC SMALL LETTER I 379 | 16#E9 -> 16#0439; %% CYRILLIC SMALL LETTER SHORT I 380 | 16#EA -> 16#043A; %% CYRILLIC SMALL LETTER KA 381 | 16#EB -> 16#043B; %% CYRILLIC SMALL LETTER EL 382 | 16#EC -> 16#043C; %% CYRILLIC SMALL LETTER EM 383 | 16#ED -> 16#043D; %% CYRILLIC SMALL LETTER EN 384 | 16#EE -> 16#043E; %% CYRILLIC SMALL LETTER O 385 | 16#EF -> 16#043F; %% CYRILLIC SMALL LETTER PE 386 | 16#F0 -> 16#0440; %% CYRILLIC SMALL LETTER ER 387 | 16#F1 -> 16#0441; %% CYRILLIC SMALL LETTER ES 388 | 16#F2 -> 16#0442; %% CYRILLIC SMALL LETTER TE 389 | 16#F3 -> 16#0443; %% CYRILLIC SMALL LETTER U 390 | 16#F4 -> 16#0444; %% CYRILLIC SMALL LETTER EF 391 | 16#F5 -> 16#0445; %% CYRILLIC SMALL LETTER HA 392 | 16#F6 -> 16#0446; %% CYRILLIC SMALL LETTER TSE 393 | 16#F7 -> 16#0447; %% CYRILLIC SMALL LETTER CHE 394 | 16#F8 -> 16#0448; %% CYRILLIC SMALL LETTER SHA 395 | 16#F9 -> 16#0449; %% CYRILLIC SMALL LETTER SHCHA 396 | 16#FA -> 16#044A; %% CYRILLIC SMALL LETTER HARD SIGN 397 | 16#FB -> 16#044B; %% CYRILLIC SMALL LETTER YERU 398 | 16#FC -> 16#044C; %% CYRILLIC SMALL LETTER SOFT SIGN 399 | 16#FD -> 16#044D; %% CYRILLIC SMALL LETTER E 400 | 16#FE -> 16#044E; %% CYRILLIC SMALL LETTER YU 401 | 16#FF -> 16#044F; %% CYRILLIC SMALL LETTER YA 402 | Other -> Other 403 | end, 404 | win_to_utf8(Rest, <>); 405 | win_to_utf8(<<>>, Acc) -> 406 | Acc. 407 | -------------------------------------------------------------------------------- /src/uef_file.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2019-2024, Sergei Semichev . All Rights Reserved. 2 | %% 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | 15 | -module(uef_file). 16 | 17 | -include_lib("kernel/include/file.hrl"). 18 | 19 | -export([read_file_fast/1, read_file_info_fast/1]). 20 | 21 | %%%------------------------------------------------------------------------------ 22 | %%% Types 23 | %%%------------------------------------------------------------------------------ 24 | 25 | -type file_name() :: file:name_all(). 26 | -type file_info() :: file:file_info(). 27 | 28 | %%%------------------------------------------------------------------------------ 29 | %%% API 30 | %%%------------------------------------------------------------------------------ 31 | 32 | %% read_file_info_fast/1 33 | %% Should return the same as file:read_file_info/1,2 34 | %% http://erlang.org/doc/man/file.html#read_file_info-1 35 | -spec read_file_info_fast(Filename :: file_name()) -> {ok, FileInfo :: file_info()} | {error, Reason :: any()}. 36 | %% @doc 37 | %% Retrieves information about local file. 38 | %% Returns {ok, FileInfo} if successful, otherwise {error, Reason}. 39 | %% Works as file:read_file_info/2 but optimized for local files. This is a wrapper of: 40 | %% file:read_file_info(Filename, [raw, {time, posix}]). 41 | %% @end 42 | read_file_info_fast(Filename) -> 43 | file:read_file_info(Filename, [raw, {time, posix}]). 44 | 45 | %% read_file_fast/1 46 | %% Should return the same as file:read_file/1 47 | %% http://erlang.org/doc/man/file.html#read_file-1 48 | -spec read_file_fast(Filename :: file_name()) -> {ok, BinaryData :: binary()} | {error, Reason :: atom()}. 49 | %% @doc 50 | %% Reads contents of local file Filename 51 | %% and returns {ok, BinaryData}, where BinaryData is a binary data object that contains the contents of Filename, or {error, Reason} if an error occurs. 52 | %% This function is optimized for reading contents of local files, as no Erlang process is used. 53 | %% It calls file:open/2 with options [read, raw, binary]. 54 | %% @end 55 | read_file_fast(Filename) -> 56 | case read_file_info_fast(Filename) of 57 | {ok, #file_info{size = Filesize}} -> 58 | case open_and_read_bytes(Filename, Filesize) of 59 | {ok, _} = Result -> Result; 60 | eof -> {error, eof}; 61 | {error, _} = Error -> Error 62 | end; 63 | {error, _} = Error -> 64 | Error 65 | end. 66 | 67 | 68 | %%%------------------------------------------------------------------------------ 69 | %%% Internal functions 70 | %%%------------------------------------------------------------------------------ 71 | 72 | %% open_and_read_bytes/2 73 | %% Should return the same as file:read/2 74 | %% http://erlang.org/doc/man/file.html#read-2 75 | -spec open_and_read_bytes(file_name(), integer()) -> {ok, binary()} | eof | {error, any()}. 76 | open_and_read_bytes(Filename, Bytes) -> 77 | case file:open(Filename, [read, raw, binary]) of %% {ok, IoDevice} | {error, Reason} 78 | {ok, Fd} -> 79 | ReadResult = file:read(Fd, Bytes), %% {ok, Data} | eof | {error, Reason} 80 | _ = file:close(Fd), 81 | ReadResult; 82 | {error, _} = Error -> 83 | Error 84 | end. 85 | -------------------------------------------------------------------------------- /src/uef_format.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2019-2024, Sergei Semichev . All Rights Reserved. 2 | %% 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | 15 | -module(uef_format). 16 | 17 | -export([format_number/3, format_number/4]). 18 | -export([format_price/1, format_price/2, format_price/3]). 19 | -export([format_bytes/1, format_bytes/2]). 20 | 21 | %%%------------------------------------------------------------------------------ 22 | %%% Macros 23 | %%%------------------------------------------------------------------------------ 24 | 25 | -define(DEFAULT_PRICE_PRECISION, 2). 26 | -define(DEFAULT_PRICE_DECIMALS, 2). 27 | -define(THOUSANDS_SEP, <<"">>). 28 | -define(DECIMAL_POINT, <<".">>). 29 | -define(CURRENCY_POSITION, left). 30 | -define(CURRENCY_SEP, <<"">>). 31 | -define(MULTI_BYTE_UNITS, ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']). 32 | 33 | 34 | %%%------------------------------------------------------------------------------ 35 | %%% Types 36 | %%%------------------------------------------------------------------------------ 37 | 38 | -type formatted_number() :: binary(). 39 | -type precision() :: integer(). 40 | -type decimals() :: 0..253. % see types for erlang:float_to_binary/2 41 | -type cur_symbol() :: binary() | string(). 42 | -type format_number_opts() :: #{ 43 | thousands_sep => binary() | string(), 44 | decimal_point => binary() | string(), 45 | cur_symbol => cur_symbol(), 46 | cur_pos => left | right, 47 | cur_sep => binary() | string() 48 | }. 49 | -type multi_byte_unit() :: 'KB' | 'MB' | 'GB' | 'TB' | 'PB' | 'EB' | 'ZB' | 'YB'. 50 | -type byte_opts_in() :: #{ 51 | base => 2 | 10, 52 | units => auto | multi_byte_unit(), 53 | to_type => bin | int, 54 | sep => binary() 55 | }. 56 | -type valid_byte_opts() :: #{ 57 | base => 1000 | 1024, 58 | units => auto | multi_byte_unit(), 59 | to_type => bin | int, 60 | sep => binary() 61 | }. 62 | -type formatted_bytes() :: binary() | integer() | {integer(), multi_byte_unit()}. 63 | -type byte_opts_error() :: {invalid_base|invalid_units|invalid_output_type|invalid_separator, term()}. 64 | 65 | %%%------------------------------------------------------------------------------ 66 | %%% API 67 | %%%------------------------------------------------------------------------------ 68 | 69 | %% format_number/3 70 | -spec format_number(number(), precision(), decimals()) -> formatted_number(). 71 | %% @doc 72 | %% The same as uef_format:format_number/4 with #{} as the forth argument. See uef_format:format_number/4 docs. 73 | %% @end 74 | format_number(Number, Precision, Decimals) -> 75 | format_number(Number, Precision, Decimals, #{}). 76 | 77 | %% format_number/4 78 | -spec format_number(number(), precision(), decimals(), format_number_opts()) -> formatted_number(). 79 | %% @doc 80 | %% Formats Number by adding thousands separator between each set of 3 digits to the left of the decimal point, 81 | %% substituting Decimals for the decimal point, and rounding to the specified Precision. 82 | %% Returns a binary value. 83 | %% @end 84 | format_number(Number, Precision, Decimals, Opts) when is_integer(Number) -> 85 | format_number(erlang:float(Number), Precision, Decimals, Opts); 86 | format_number(Number, Precision, Decimals, Opts) when is_float(Number) -> 87 | Precision2 = case Precision > 0 andalso Decimals < Precision of 88 | true -> Decimals; 89 | false -> Precision 90 | end, 91 | RoundedNumber = uef_num:round_number(Number, Precision2), % round to Precision2 before formatting 92 | do_format_number(RoundedNumber, Decimals, Opts). 93 | 94 | %% format_price/1 95 | -spec format_price(Number:: number()) -> FormattedPrice :: formatted_number(). 96 | %% @doc 97 | %% Formats Number in price-like style. 98 | %% Returns a binary containing FormattedPrice formatted with a precision of 2 and decimal digits of 2. 99 | %% The same as uef_format:format_price/2 with a precision of 2 as the second argument. See uef_format:format_price/2 docs. 100 | %% @end 101 | format_price(Price) -> 102 | format_price(Price, ?DEFAULT_PRICE_PRECISION). 103 | 104 | %% format_price/2 105 | -spec format_price(Number :: number(), Precision :: precision()) -> FormattedPrice :: formatted_number(). 106 | %% @doc 107 | %% Formats Number in price-like style. 108 | %% Returns a binary containing FormattedPrice formatted with a specified precision as the second argument and decimal digits of 2. 109 | %% The same as uef_format:format_price/3 with #{} as the third argument. See uef_format:format_price/3 docs. 110 | %% @end 111 | format_price(Price, Precision) -> 112 | format_price(Price, Precision, #{}). 113 | 114 | %% format_price/3 115 | -spec format_price(Number :: number(), Precision :: precision(), CurrencySymbol_OR_Options :: format_number_opts() | cur_symbol()) -> FormattedPrice :: formatted_number(). 116 | %% @doc 117 | %% Formats Number in price-like style. 118 | %% Returns a binary containing FormattedPrice formatted with a specified precision as the second argument, decimal digits of 2, 119 | %% and with currency symbol (or options) as the third argument. 120 | %% If CurrencySymbol_OR_Options is a map the functions works as uef_format:format_number/4 121 | %% with decimal digits of 2 as the third argument and with options as the forth one. 122 | %% If CurrencySymbol_OR_Options is a binary or a string, the corresponding currency symbol is added to the left. 123 | %% @end 124 | format_price(Price, Precision, Opts) when is_map(Opts) -> 125 | format_number(Price, Precision, ?DEFAULT_PRICE_DECIMALS, Opts); 126 | format_price(Price, Precision, CurSymbol) when is_binary(CurSymbol) orelse is_list(CurSymbol) -> 127 | format_number(Price, Precision, ?DEFAULT_PRICE_DECIMALS, #{cur_symbol => CurSymbol}); 128 | format_price(Price, Precision, Opts) -> 129 | erlang:error({badarg, Opts}, [Price, Precision, Opts]). 130 | 131 | 132 | %% format_bytes/1 133 | -spec format_bytes(integer()) -> formatted_bytes(). 134 | %% @equiv format_bytes(Bytes, #{}) 135 | format_bytes(Bytes) -> 136 | format_bytes(Bytes, #{}). 137 | 138 | %% format_bytes/2 139 | %% @doc 140 | %% Converts bytes to multiples of bytes (KB, MB, GB, TB, PB, EB, ZB, YB). 141 | %% See README for details. 142 | %% @end 143 | -spec format_bytes(integer(), byte_opts_in()) -> formatted_bytes(). 144 | format_bytes(Bytes, Opts0) when is_integer(Bytes), is_map(Opts0) -> 145 | case validate_byte_opts(Opts0) of 146 | {ok, Opts} -> 147 | do_format_bytes(Bytes, Opts); 148 | {error, Reason} -> 149 | erlang:error(Reason, [Bytes, Opts0]) 150 | end; 151 | format_bytes(Bytes, Opts) -> 152 | BadArg = case is_integer(Bytes) of 153 | true -> Opts; 154 | false -> Bytes 155 | end, 156 | erlang:error({badarg, BadArg}, [Bytes, Opts]). 157 | 158 | 159 | %%%------------------------------------------------------------------------------ 160 | %%% Internal functions 161 | %%%------------------------------------------------------------------------------ 162 | 163 | %% do_format_number/3 164 | -spec do_format_number(number(), decimals(), format_number_opts()) -> formatted_number(). 165 | do_format_number(Number, Decimals, Opts) -> 166 | PositiveNumber = case Number < 0 of 167 | false -> Number; 168 | true -> erlang:abs(Number) 169 | end, 170 | BinNum = erlang:float_to_binary(PositiveNumber, [{decimals, Decimals}]), 171 | {IntegerPart, DecimalPart} = case uef_bin:split(BinNum, <<".">>) of 172 | [I, D] -> {I, D}; % ex: <<"12.345">> -> [<<"12">>, <<"345">>] 173 | [I] -> {I, <<>>} % ex: <<"12345">> -> [<<"12345">>] (when Precision < 1) 174 | end, 175 | HeadSize = erlang:byte_size(IntegerPart) rem 3, 176 | <> = IntegerPart, % ex: <<"12", "345678">> = <<"12345678">> 177 | ThousandParts = split_thousands(IntRest), % ex: <<"345678">> -> [<<"345">>, <<678>>] 178 | AllIntegerParts = case HeadSize > 0 of 179 | true -> [Head|ThousandParts]; 180 | false -> ThousandParts 181 | end, 182 | ThousandsSep = maybe_to_binary(maps:get(thousands_sep, Opts, ?THOUSANDS_SEP)), 183 | % Join with thousands separator 184 | FormattedIntegerPart = <<(uef_bin:binary_join(AllIntegerParts, ThousandsSep))/binary>>, 185 | PositiveFormattedNumber = case DecimalPart of 186 | <<>> -> 187 | FormattedIntegerPart; 188 | _ -> 189 | DecimalPoint = maybe_to_binary(maps:get(decimal_point, Opts, ?DECIMAL_POINT)), 190 | <> 191 | end, 192 | % Insert "-" before number if negative 193 | FormattedNumber1 = case Number < 0 of 194 | false -> PositiveFormattedNumber; 195 | true -> <<"-", PositiveFormattedNumber/binary>> 196 | end, 197 | % Format with currency options 198 | format_number_with_currency(FormattedNumber1, Opts). 199 | 200 | %% format_number_with_currency/2 201 | -spec format_number_with_currency(binary(), map()) -> binary(). 202 | format_number_with_currency(FmtNum, #{cur_symbol := CurSymbol0} = Opts) -> 203 | CurSymbol = maybe_to_binary(CurSymbol0), 204 | CurSep = maybe_to_binary(maps:get(cur_sep, Opts, ?CURRENCY_SEP)), 205 | case maps:get(cur_pos, Opts, ?CURRENCY_POSITION) of 206 | left -> <>; 207 | _ -> <> 208 | end; 209 | format_number_with_currency(FmtNum, _) -> 210 | FmtNum. 211 | 212 | %% maybe_to_binary/1 213 | -spec maybe_to_binary(binary() | string()) -> binary(). 214 | maybe_to_binary(B) when is_binary(B) -> 215 | B; 216 | maybe_to_binary(L) when is_list(L) -> 217 | case unicode:characters_to_binary(L, utf8, utf8) of 218 | B when is_binary(B) -> B; 219 | _ -> erlang:error(badarg) 220 | end; 221 | maybe_to_binary(_)-> 222 | erlang:error(badarg). 223 | 224 | 225 | %% split_thousands/1 226 | -spec split_thousands(binary()) -> [<<_:24>>]. 227 | split_thousands(Bin) -> 228 | split_thousands(Bin, []). 229 | 230 | %% split_thousands/2 231 | -spec split_thousands(binary(), [<<_:24>>]) -> [<<_:24>>]. 232 | split_thousands(<<>>, List) -> 233 | lists:reverse(List); 234 | split_thousands(<>, List) -> 235 | split_thousands(Rest, [B | List]). 236 | 237 | 238 | %% do_format_bytes/2 239 | -spec do_format_bytes(integer(), valid_byte_opts()) -> formatted_bytes(). 240 | do_format_bytes(Bytes, Opts) -> 241 | #{base := Base, units := Units0, to_type := Type, sep := Sep} = Opts, 242 | {MultiBytes, Units} = bytes_to_multiple(Bytes, Units0, Base), 243 | case Type of 244 | bin -> 245 | BinMultiBytes = erlang:integer_to_binary(MultiBytes), 246 | BinUnits = erlang:atom_to_binary(Units, latin1), 247 | <>; 248 | int when (Units0 =:= auto) -> 249 | {MultiBytes, Units}; 250 | int -> 251 | MultiBytes 252 | end. 253 | 254 | %% bytes_to_multiple/3 255 | -spec bytes_to_multiple(integer(), auto | multi_byte_unit(), 1000 | 1024) -> {integer(), multi_byte_unit()}. 256 | bytes_to_multiple(Bytes, Units, Base) -> 257 | bytes_to_multiple(Bytes, Units, Base, ?MULTI_BYTE_UNITS, 0, 'KB', 1). 258 | 259 | 260 | %% bytes_to_multiple/7 261 | -spec bytes_to_multiple(integer(), auto | multi_byte_unit(), 1000 | 1024, [multi_byte_unit()], integer(), multi_byte_unit(), pos_integer()) -> 262 | {integer(), multi_byte_unit()}. 263 | bytes_to_multiple(_Bytes, _Units0, _Base, [], MultiBytes, Units, _Pow) -> 264 | {MultiBytes, Units}; 265 | bytes_to_multiple(Bytes, Units0, Base, [CurUnits | Tail], MultiBytesBefore, UnitsBefore, Pow) -> 266 | MultiBytes = erlang:trunc(Bytes/math:pow(Base, Pow)), 267 | case Units0 of 268 | CurUnits -> 269 | {MultiBytes, Units0}; 270 | auto when (MultiBytes =:= 0) -> 271 | {MultiBytesBefore, UnitsBefore}; 272 | _ -> 273 | bytes_to_multiple(Bytes, Units0, Base, Tail, MultiBytes, CurUnits, Pow + 1) 274 | end. 275 | 276 | 277 | %% validate_byte_opts/1 278 | -spec validate_byte_opts(byte_opts_in()) -> {ok, valid_byte_opts()} | {error, byte_opts_error()}. 279 | validate_byte_opts(Opts0) -> 280 | validate_byte_opts([base, units, to_type, sep], Opts0, #{}). 281 | 282 | %% validate_byte_opts/2 283 | -spec validate_byte_opts([base|units|to_type|sep,...], byte_opts_in(), valid_byte_opts()) -> {ok, valid_byte_opts()} | {error, byte_opts_error()}. 284 | validate_byte_opts([], _Opts0, Acc) -> 285 | {ok, Acc}; 286 | validate_byte_opts([base|Tail], Opts0, Acc) -> % base 287 | case maps:find(base, Opts0) of 288 | error -> validate_byte_opts(Tail, Opts0, Acc#{base => 1024}); % default 289 | {ok, 2} -> validate_byte_opts(Tail, Opts0, Acc#{base => 1024}); 290 | {ok, 10} -> validate_byte_opts(Tail, Opts0, Acc#{base => 1000}); 291 | {ok, Base} -> {error, {invalid_base, Base}} 292 | end; 293 | validate_byte_opts([units|Tail], Opts0, Acc) -> % units 294 | case maps:find(units, Opts0) of 295 | error -> % Units not specified, set them to 'auto' 296 | validate_byte_opts(Tail, Opts0, Acc#{units => auto}); 297 | {ok, auto} -> % auto 298 | validate_byte_opts(Tail, Opts0, Acc#{units => auto}); 299 | {ok, Units} -> % Units specified, check them 300 | case lists:member(Units, ?MULTI_BYTE_UNITS) of 301 | true -> validate_byte_opts(Tail, Opts0, Acc#{units => Units}); 302 | false -> {error, {invalid_units, Units}} 303 | end 304 | end; 305 | validate_byte_opts([to_type|Tail], Opts0, Acc) -> % to_type 306 | case maps:find(to_type, Opts0) of 307 | error -> % Output type not specified, set it to 'bin' 308 | validate_byte_opts(Tail, Opts0, Acc#{to_type => 'bin'}); 309 | {ok, Type} when (Type =:= int) orelse (Type =:= bin) -> 310 | validate_byte_opts(Tail, Opts0, Acc#{to_type => Type}); 311 | {ok, Type} -> 312 | {error, {invalid_output_type, Type}} 313 | end; 314 | validate_byte_opts([sep|Tail], Opts0, Acc) -> % separator 315 | case maps:find(sep, Opts0) of 316 | error -> % Separator not specified, set it to <<>> (empty binary) 317 | validate_byte_opts(Tail, Opts0, Acc#{sep => <<>>}); 318 | {ok, Sep} when is_binary(Sep) -> 319 | validate_byte_opts(Tail, Opts0, Acc#{sep => Sep}); 320 | {ok, Sep} -> 321 | {error, {invalid_separator, Sep}} 322 | end. 323 | -------------------------------------------------------------------------------- /src/uef_lists.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2019-2024, Sergei Semichev . All Rights Reserved. 2 | %% 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | 15 | -module(uef_lists). 16 | 17 | -export([split_list_into_chunks/2]). 18 | -export([lists_to_list_of_tuples/2, lists_to_list_of_tuples/3]). 19 | -export([search/2]). 20 | 21 | %%%------------------------------------------------------------------------------ 22 | %%% API 23 | %%%------------------------------------------------------------------------------ 24 | 25 | %% split_list_into_chunks/2 26 | -spec split_list_into_chunks(List :: list(), MaxLen :: pos_integer()) -> List2 :: list(). 27 | %% @doc 28 | %% Splits List into list of lists [List1, List2, ..., ListN] 29 | %% where List1, List2, ..., ListN are lists with maximum MaxLen elements. 30 | %% @end 31 | split_list_into_chunks([],_) -> []; 32 | split_list_into_chunks(List,Len) when Len > length(List) -> 33 | [List]; 34 | split_list_into_chunks(List,Len) -> 35 | {Head,Tail} = lists:split(Len,List), 36 | [Head | split_list_into_chunks(Tail,Len)]. 37 | 38 | 39 | %% lists_to_list_of_tuples/2 40 | -spec lists_to_list_of_tuples(List1 :: list(), List2 :: list()) -> List3 :: [tuple()]. 41 | %% @doc 42 | %% Transforms two lists into one list of two-tuples, 43 | %% where the first element of each tuple is taken from the first list 44 | %% and the second element is taken from the second list one by one. 45 | %% @end 46 | lists_to_list_of_tuples(List1, List2) -> 47 | List = lists:foldl( 48 | fun(Elem1, Acc1) -> 49 | lists:foldl( 50 | fun(Elem2, Acc2) -> 51 | [{Elem1, Elem2} | Acc2] 52 | end, 53 | Acc1, List2 54 | ) 55 | end, 56 | [], List1 57 | ), 58 | lists:reverse(List). 59 | 60 | %% lists_to_list_of_tuples/3 61 | -spec lists_to_list_of_tuples(List1 :: list(), List2 :: list(), List3 :: list()) -> List4 :: [tuple()]. 62 | %% @doc 63 | %% Transforms three lists into one list of three-tuples, 64 | %% where the first element of each tuple is taken from the first list, 65 | %% the second element is taken from the second list one by one, 66 | %% and the third element is taken from the third list one by one. 67 | %% @end 68 | lists_to_list_of_tuples(List1, List2, List3) -> 69 | List = lists:foldl( 70 | fun(Elem1, Acc1) -> 71 | lists:foldl( 72 | fun(Elem2, Acc2) -> 73 | lists:foldl( 74 | fun(Elem3, Acc3) -> 75 | [{Elem1, Elem2, Elem3} | Acc3] 76 | end, 77 | Acc2, List3 78 | ) 79 | end, 80 | Acc1, List2 81 | ) 82 | end, 83 | [], List1 84 | ), 85 | lists:reverse(List). 86 | 87 | %% search/2 88 | -spec search(Pred, List) -> {value, Value} | false when 89 | Pred :: fun((T) -> boolean()), 90 | List :: [T], 91 | Value :: T. 92 | %% @doc 93 | %% If there is a Value in List such that Pred(Value) returns true, returns {value, Value} for the first such Value, otherwise returns false. 94 | %% Since OTP 21.0 use BIF lists:search/2 instead. 95 | %% @end 96 | search(Pred, [Hd|Tail]) -> 97 | case Pred(Hd) of 98 | true -> {value, Hd}; 99 | false -> search(Pred, Tail) 100 | end; 101 | search(Pred, []) when is_function(Pred, 1) -> 102 | false. 103 | -------------------------------------------------------------------------------- /src/uef_maps.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2019-2024, Sergei Semichev . All Rights Reserved. 2 | %% 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | 15 | -module(uef_maps). 16 | 17 | -export([delete_nested/2]). 18 | -export([find_nested/2]). 19 | -export([get_nested/2, get_nested/3]). 20 | -export([new_nested/1, new_nested/2]). 21 | -export([is_key_nested/2]). 22 | -export([put_nested/3]). 23 | -export([update_nested/3]). 24 | -export([remove_nested/2]). 25 | -export([take_nested/2]). 26 | 27 | %%%------------------------------------------------------------------------------ 28 | %%% Types 29 | %%%------------------------------------------------------------------------------ 30 | 31 | -type mapkey() :: term(). 32 | -type mapkeys() :: [mapkey()]. 33 | -type find_result() :: {ok, term()} | error. 34 | 35 | 36 | %%%------------------------------------------------------------------------------ 37 | %%% API 38 | %%%------------------------------------------------------------------------------ 39 | 40 | %% find_nested/2 41 | -spec find_nested(mapkeys(), map()) -> find_result(). 42 | %% @doc 43 | %% Returns tuple {ok, Value}, where Value is the value associated with the last element of list Keys, or error if no value is found. 44 | %% The call fails with a {badmap,Map} exception if Map is not a map, or with a {badlist,Keys} exception if Keys is not a list. 45 | %% @end 46 | find_nested(Keys, Map) -> 47 | find_nested_unsafe(Keys, Map). 48 | 49 | 50 | %% get_nested/2 51 | -spec get_nested(Keys :: mapkeys(), Map :: map()) -> Value :: term(). 52 | %% @doc 53 | %% Returns value Value associated with the last element of list Keys. 54 | %% The call fails with a {badmap,Map} exception if Map is not a map, 55 | %% or with a {badkeys,Keys} exception if no value is found, 56 | %% or with a {badlist,Keys} exception if Keys is not a list. 57 | %% @end 58 | get_nested(Keys, Map) -> 59 | FindResult = find_nested_unsafe(Keys, Map), % may fail here with a {badmap,Map} exception 60 | case FindResult of 61 | {ok, Value} -> Value; 62 | error -> erlang:error({badkeys,Keys},[Keys,Map]) 63 | end. 64 | 65 | %% get_nested/3 66 | -spec get_nested(Keys :: mapkeys(), Map :: map(), Default :: term()) -> Value :: term(). 67 | %% @doc 68 | %% Returns value Value associated with the last element of list Keys. 69 | %% If no value is found, Default is returned. 70 | %% The call fails with a {badmap,Map} exception if Map is not a map, 71 | %% or with a {badlist,Keys} exception if Keys is not a list. 72 | %% It does not fail if any internal value associated with any element of list Keys is not a map. 73 | %% @end 74 | get_nested(Keys, Map, Default) when is_map(Map) -> 75 | FindResult = find_nested_safe(Keys, Map), 76 | case FindResult of 77 | {ok, Value} -> Value; 78 | error -> Default 79 | end; 80 | get_nested(Keys, Map, Default) -> 81 | erlang:error({badmap,Map},[Keys, Map, Default]). 82 | 83 | %% new_nested/1 84 | -spec new_nested(mapkeys()) -> map(). 85 | %% @doc 86 | %% Same as uef_maps:new_nested(Keys, #{}). See docs of uef_maps:new_nested/2. 87 | %% @end 88 | new_nested(Keys) -> 89 | new_nested(Keys, #{}). 90 | 91 | %% new_nested/2 92 | -spec new_nested(Keys :: mapkeys(), Value :: term()) -> Map :: map(). 93 | %% @doc 94 | %% Returns new nested map Map with the deepest map #{LastKey => Value}, where LastKey is the last element of list Keys. 95 | %% The call fails with a {badlist,Keys} exception if Keys is not a list. 96 | %% @end 97 | new_nested([], _) -> 98 | #{}; 99 | new_nested([Key], Value) -> 100 | #{Key => Value}; 101 | new_nested(Keys, Value) when is_list(Keys) -> 102 | [LastKey | Rest] = lists:reverse(Keys), 103 | new_nested_1(Rest, #{LastKey => Value}); 104 | new_nested(Keys, Value) -> 105 | erlang:error({badlist, Keys}, [Keys, Value]). 106 | 107 | 108 | %% is_key_nested/2 109 | -spec is_key_nested(Keys :: mapkeys(), map()) -> boolean(). 110 | %% @doc 111 | %% Returns true if map Map contains submaps as values associated with their own key corresponding to the element of list Keys, 112 | %% and returns false otherwise. 113 | %% The call fails with a {badmap,Map} exception if Map is not a map, 114 | %% or with a {badlist,Keys} exception if Keys is not a list. 115 | %% @end 116 | is_key_nested(Keys, Map) when is_list(Keys), is_map(Map) -> 117 | is_key_nested(Keys, Map, false); 118 | is_key_nested(Keys, Map) -> 119 | Args = [Keys, Map], 120 | case is_list(Keys) of 121 | true -> erlang:error({badmap, Map}, Args); 122 | false -> erlang:error({badlist, Keys}, Args) 123 | end. 124 | 125 | 126 | %% put_nested/3 127 | -spec put_nested(Keys :: mapkeys(), Value :: term(), Map1 :: map()) -> Map2 :: map(). 128 | %% @doc 129 | %% The function associates KeyN with value Value and updates the entire structure of map Map1 returning new map Map2. 130 | %% If some keys from list Keys are not in the structure of map Map1, they will be inserted into the structure of map Map2 in the same order. 131 | %% The call fails with a {badmap,Map1} exception if Map1 is not a map, or with a {badlist,Keys} exception if Keys is not a list. 132 | %% @end 133 | put_nested([], _, Map) when is_map(Map) -> 134 | Map; 135 | put_nested([Key], Value, Map) when is_map(Map) -> 136 | Map#{Key => Value}; 137 | put_nested(Keys, Value, Map) when is_list(Keys), is_map(Map) -> 138 | Tuples = nested_to_tuples_for_put(Keys, Map, []), 139 | lists:foldl(fun({K, M}, Acc) -> M#{K => Acc} end, Value, Tuples); 140 | put_nested(Keys, Value, Map) -> 141 | Args = [Keys, Value, Map], 142 | case is_list(Keys) of 143 | true -> erlang:error({badmap, Map}, Args); 144 | false -> erlang:error({badlist, Keys}, Args) 145 | end. 146 | 147 | 148 | %% update_nested/3 149 | -spec update_nested(mapkeys(), Value :: term(), map()) -> map(). 150 | %% @doc 151 | %% Works similar to uef_maps:put_nested/3 with the difference that it fails with a {badkey,SomeKey} exception 152 | %% if SomeKey does not exist in the structure of map Map1, where SomeKey is one of the elements of list Keys. 153 | %% The call also fails with a {badmap,Map1} exception if Map1 is not a map, or with a {badlist,Keys} exception if Keys is not a list. 154 | %% @end 155 | update_nested([], _, Map) when is_map(Map) -> 156 | Map; 157 | update_nested([Key], Value, Map) when is_map(Map) -> 158 | maps:update(Key, Value, Map); 159 | update_nested(Keys, Value, Map) when is_list(Keys), is_map(Map) -> 160 | case nested_to_tuples_for_update(Keys, Map, []) of 161 | {ok, Tuples} -> 162 | lists:foldl(fun({K, M}, Acc) -> maps:update(K, Acc, M) end, Value, Tuples); 163 | {error, Reason} -> 164 | erlang:error(Reason, [Keys, Value, Map]) 165 | end; 166 | update_nested(Keys, Value, Map) -> 167 | Args = [Keys, Value, Map], 168 | case is_list(Keys) of 169 | true -> erlang:error({badmap, Map}, Args); 170 | false -> erlang:error({badlist, Keys}, Args) 171 | end. 172 | 173 | 174 | %% remove_nested/2 175 | -spec remove_nested(Keys :: mapkeys(), Map1 :: map()) -> Map2 :: map(). 176 | %% @doc 177 | %% The function removes key KeyN, if it exists, and its associated value from the corresponding internal map 178 | %% and updates the entire structure of map Map1 returning new map Map2. 179 | %% If some keys from list Keys are not in the structure of map Map1 the function returns a map without changes. 180 | %% The call fails with a {badmap,Map1} exception if Map1 is not a map, or with a {badlist,Keys} exception if Keys is not a list. 181 | %% @end 182 | remove_nested([], Map) when is_map(Map) -> 183 | Map; 184 | remove_nested([Key], Map) when is_map(Map) -> 185 | maps:remove(Key, Map); 186 | remove_nested(Keys, Map) when is_list(Keys), is_map(Map) -> 187 | case nested_to_tuples_for_update(Keys, Map, []) of 188 | {ok, Tuples} -> 189 | [{LastKey, LastMap} | Rest] = Tuples, 190 | LastMap2 = maps:remove(LastKey, LastMap), 191 | lists:foldl(fun({K, M}, Acc) -> M#{K => Acc} end, LastMap2, Rest); 192 | {error, _} -> 193 | Map 194 | end; 195 | remove_nested(Keys, Map) -> 196 | Args = [Keys, Map], 197 | case is_list(Keys) of 198 | true -> erlang:error({badmap, Map}, Args); 199 | false -> erlang:error({badlist, Keys}, Args) 200 | end. 201 | 202 | 203 | %% take_nested/2 204 | -spec take_nested(Keys :: mapkeys(), Map1 :: map()) -> {Value :: term(), Map2 :: map()} | error. 205 | %% @doc 206 | %% The function removes key KeyN, if it exists, and its associated value Value from the corresponding internal map 207 | %% and updates the entire structure of map Map1 returning tuple {Value, Map2}. 208 | %% If some keys from list Keys are not in the structure of map Map1 the function returns error. 209 | %% The call fails with a {badmap,Map1} exception if Map1 is not a map, or with a {badlist,Keys} exception if Keys is not a list. 210 | %% @end 211 | take_nested([], Map) when is_map(Map) -> 212 | error; 213 | take_nested([Key], Map) when is_map(Map) -> 214 | maps:take(Key, Map); 215 | take_nested(Keys, Map) when is_list(Keys), is_map(Map) -> 216 | case nested_to_tuples_for_update(Keys, Map, []) of 217 | {ok, Tuples} -> 218 | [{LastKey, LastMap} | Rest] = Tuples, 219 | {Value, LastMap2} = maps:take(LastKey, LastMap), 220 | Map2 = lists:foldl(fun({K, M}, Acc) -> M#{K => Acc} end, LastMap2, Rest), 221 | {Value, Map2}; 222 | {error, _} -> 223 | error 224 | end; 225 | take_nested(Keys, Map) -> 226 | Args = [Keys, Map], 227 | case is_list(Keys) of 228 | true -> erlang:error({badmap, Map}, Args); 229 | false -> erlang:error({badlist, Keys}, Args) 230 | end. 231 | 232 | 233 | %% delete_nested/2 234 | -spec delete_nested(Keys :: mapkeys(), Map1 :: map()) -> {ok, Map2 :: map()} | {error, {badkey, mapkey()}} | {error, empty_keys}. 235 | %% @doc 236 | %% The function removes key KeyN, if it exists, and its associated value from the corresponding internal map 237 | %% and updates the entire structure of map Map1 getting new map Map2. KeyN is the last element of list Keys. 238 | %% @end 239 | delete_nested([], Map) when is_map(Map) -> 240 | {error, empty_keys}; 241 | delete_nested([Key], Map) when is_map(Map) -> 242 | case maps:is_key(Key, Map) of 243 | true -> {ok, maps:remove(Key, Map)}; 244 | false -> {error, {badkey, Key}} 245 | end; 246 | delete_nested(Keys, Map) when is_list(Keys), is_map(Map) -> 247 | case nested_to_tuples_for_update(Keys, Map, []) of 248 | {ok, Tuples} -> 249 | [{LastKey, LastMap} | Rest] = Tuples, 250 | LastMap2 = maps:remove(LastKey, LastMap), 251 | Map2 = lists:foldl(fun({K, M}, Acc) -> M#{K => Acc} end, LastMap2, Rest), 252 | {ok, Map2}; 253 | {error, _} = Error -> 254 | Error 255 | end; 256 | delete_nested(Keys, Map) -> 257 | Args = [Keys, Map], 258 | case is_list(Keys) of 259 | true -> erlang:error({badmap, Map}, Args); 260 | false -> erlang:error({badlist, Keys}, Args) 261 | end. 262 | 263 | %%%------------------------------------------------------------------------------ 264 | %%% Internal functions 265 | %%%------------------------------------------------------------------------------ 266 | 267 | %% find_nested_unsafe/2 268 | -spec find_nested_unsafe(mapkeys(), map()) -> find_result(). 269 | find_nested_unsafe(Keys, Map) -> 270 | find_nested(Keys, Map, unsafe, error). 271 | 272 | %% find_nested_safe/2 273 | -spec find_nested_safe(mapkeys(), map()) -> find_result(). 274 | find_nested_safe(Keys, Map) -> 275 | find_nested(Keys, Map, safe, error). 276 | 277 | %% find_nested/4 278 | -spec find_nested(mapkeys(), map(), safe | unsafe, find_result()) -> find_result(). 279 | find_nested([], _, _, Result) -> 280 | Result; 281 | find_nested([Key|Tail], Map, Safe, _) -> 282 | Result = case {is_map(Map), Safe} of 283 | {false, safe} -> error; % don't fail, return 'error' safely 284 | _ -> maps:find(Key, Map) % may fail here (when {false, unsafe}) with a {badmap,Map} exception 285 | end, 286 | case Result of 287 | {ok, NestedMap} -> find_nested(Tail, NestedMap, Safe, Result); 288 | error -> error 289 | end; 290 | find_nested(NotList, _, _, _) -> 291 | erlang:error({badlist, NotList}). 292 | 293 | %% new_nested_1/2 294 | -spec new_nested_1(mapkeys(), map()) -> map(). 295 | new_nested_1([], Map) -> Map; 296 | new_nested_1([Key|Tail], Map) -> 297 | new_nested_1(Tail, #{Key => Map}). 298 | 299 | %% is_key_nested/3 300 | -spec is_key_nested(mapkeys(), map(), boolean()) -> boolean(). 301 | is_key_nested([], _, Bool) -> 302 | Bool; 303 | is_key_nested([Key|Tail], Map, _) -> 304 | case Map of 305 | #{Key := Value} -> is_key_nested(Tail, Value, true); 306 | _ -> false 307 | end. 308 | 309 | %% nested_to_tuples_for_put/3 310 | -spec nested_to_tuples_for_put(mapkeys(), term(), Tuples) -> Tuples when Tuples :: [{mapkey(), map()}]. 311 | nested_to_tuples_for_put([], _Map, Tuples) -> 312 | Tuples; 313 | nested_to_tuples_for_put([Key|Tail], Map, Tuples) -> 314 | case Map of 315 | #{Key := M} -> 316 | nested_to_tuples_for_put(Tail, M, [{Key, Map}|Tuples]); 317 | _ when is_map(Map) -> 318 | nested_to_tuples_for_put(Tail, #{}, [{Key, Map}|Tuples]); 319 | _ -> 320 | nested_to_tuples_for_put(Tail, #{}, [{Key, #{}}|Tuples]) 321 | end. 322 | 323 | %% nested_to_tuples_for_update/3 324 | -spec nested_to_tuples_for_update(mapkeys(), term(), Tuples) -> {ok, Tuples} | {error, {badkey, mapkey()}} 325 | when Tuples :: [{mapkey(), map()}]. 326 | nested_to_tuples_for_update([], _Map, Tuples) -> 327 | {ok, Tuples}; 328 | nested_to_tuples_for_update([Key|Tail], Map, Tuples) -> 329 | case Map of 330 | #{Key := M} -> 331 | nested_to_tuples_for_update(Tail, M, [{Key, Map}|Tuples]); 332 | _ -> 333 | {error, {badkey, Key}} 334 | end. 335 | -------------------------------------------------------------------------------- /src/uef_num.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2019-2024, Sergei Semichev . All Rights Reserved. 2 | %% 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | 15 | -module(uef_num). 16 | 17 | -export([round_price/1, round_number/2]). 18 | -export([popcount/1, msb_pos/1, lsb_pos/1, ctz/1]). 19 | 20 | 21 | %%%------------------------------------------------------------------------------ 22 | %%% API 23 | %%%------------------------------------------------------------------------------ 24 | 25 | %% round_price/1 26 | -spec round_price(Number :: number()) -> float(). 27 | %% @doc 28 | %% Rounds the number to the precision of 2. The same as uef_num:round_number(Number, 2). 29 | %% @end 30 | round_price(Price) -> round_number(Price, 2). 31 | 32 | %% round_number/2 33 | -spec round_number(Number :: number(), Precision :: integer()) -> float(). 34 | %% @doc 35 | %% Rounds the number to the specified precision. 36 | %% @end 37 | round_number(Number, Precision) -> 38 | P = math:pow(10, Precision), 39 | erlang:round(Number * P) / P. 40 | 41 | 42 | %% popcount/1 43 | -spec popcount(Integer:: non_neg_integer()) -> OneBits :: non_neg_integer(). 44 | %% @doc 45 | %% Returns the number of 1's (ones or one-bits) in the binary representation of a non-negative integer. 46 | %% Also known as population count, pop count, popcount, sideways sum, bit summation, 47 | %% or Hamming weight. 48 | %% The call fails with a {badarg,Integer} exception if Integer is not a non-negative integer. 49 | %% @end 50 | popcount(N) when is_integer(N) andalso (N > -1) -> 51 | popcount(N, 0); 52 | popcount(N) -> 53 | erlang:error({badarg, N}, [N]). 54 | 55 | %% lsb_pos/1 56 | -spec lsb_pos(Integer:: pos_integer()) -> Pos :: pos_integer(). 57 | %% @doc 58 | %% Returns the position of the least significant bit in the binary representation of a positive integer. 59 | %% The call fails with a {badarg,Integer} exception if Integer is not a positive integer. 60 | %% @end 61 | lsb_pos(N) when is_integer(N) andalso (N > 0) -> 62 | lsb_pos(N, 1); 63 | lsb_pos(N) -> 64 | erlang:error({badarg, N}, [N]). 65 | 66 | %% msb_pos/1 67 | -spec msb_pos(Integer:: pos_integer()) -> Pos :: pos_integer(). 68 | %% @doc 69 | %% Returns the position of the most significant bit in the binary representation of a positive integer. 70 | %% The call fails with a {badarg,Integer} exception if Integer is not a positive integer. 71 | %% @end 72 | msb_pos(N) when is_integer(N) andalso (N > 0) -> 73 | msb_pos(N, 0); 74 | msb_pos(N) -> 75 | erlang:error({badarg, N}, [N]). 76 | 77 | %% ctz/1 78 | -spec ctz(Integer:: pos_integer()) -> TrailingZeros :: non_neg_integer(). 79 | %% @doc 80 | %% Counts trailing zeros in the binary representation of a positive integer. 81 | %% Returns the number of zero bits following the least significant one bit. 82 | %% The call fails with a {badarg,Integer} exception if Integer is not a positive integer. 83 | %% @end 84 | ctz(N) -> 85 | lsb_pos(N) - 1. 86 | 87 | 88 | %%%------------------------------------------------------------------------------ 89 | %%% Internal functions 90 | %%%------------------------------------------------------------------------------ 91 | 92 | %% popcount/2 93 | -spec popcount(non_neg_integer(), non_neg_integer()) -> non_neg_integer(). 94 | popcount(0, Cnt) -> Cnt; 95 | popcount(N, Cnt) -> popcount(N band (N - 1), Cnt + 1). 96 | 97 | 98 | %% lsb_pos/2 99 | -spec lsb_pos(pos_integer(), pos_integer()) -> pos_integer(). 100 | lsb_pos(N, Cnt) when ((N band 1) =:= 1) -> Cnt; 101 | lsb_pos(N, Cnt) -> lsb_pos(N bsr 1, Cnt + 1). 102 | 103 | %% msb_pos/2 104 | -spec msb_pos(non_neg_integer(), non_neg_integer()) -> pos_integer(). 105 | msb_pos(0, Cnt) -> Cnt; 106 | msb_pos(N, Cnt) -> msb_pos(N bsr 1, Cnt + 1). 107 | -------------------------------------------------------------------------------- /src/uef_time.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2019-2024, Sergei Semichev . All Rights Reserved. 2 | %% 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | 15 | -module(uef_time). 16 | 17 | -export([add_seconds/1, add_seconds/2]). 18 | -export([add_minutes/1, add_minutes/2]). 19 | -export([add_hours/1, add_hours/2]). 20 | -export([add_days/1, add_days/2]). 21 | -export([add_weeks/1, add_weeks/2]). 22 | -export([add_months/1, add_months/2]). 23 | -export([add_years/1, add_years/2]). 24 | -export([add_time/1, add_time/2]). 25 | -export([today/0, tomorrow/0, yesterday/0]). 26 | -export([days_diff/1, days_diff/2]). 27 | -export([seconds_diff/1, seconds_diff/2]). 28 | -export([unix_time/0, unix_time/1]). 29 | 30 | %%%------------------------------------------------------------------------------ 31 | %%% Macros 32 | %%%------------------------------------------------------------------------------ 33 | 34 | -define(UNIX_EPOCH_GREGORIAN_SECONDS, calendar:datetime_to_gregorian_seconds({{1970,1,1}, {0,0,0}})). 35 | 36 | %%%------------------------------------------------------------------------------ 37 | %%% Types 38 | %%%------------------------------------------------------------------------------ 39 | 40 | -type date() :: calendar:date(). % {Year, Month, Day} 41 | -type datetime() :: calendar:datetime(). % {{Year, Month, Day}, {Hour, Min, Sec}} 42 | -type psecond() :: sec | second | seconds. 43 | -type pminute() :: min | minute | minutes. 44 | -type phour() :: hrs | hour | hours. 45 | -type pday() :: day | days. 46 | -type pmonth() :: month | months. 47 | -type pyear() :: year | years. 48 | -type ptype() :: psecond() | pminute() | phour() | pday() | pmonth() | pyear(). 49 | -type period() :: {integer(), ptype()} | {ptype(), integer()}. 50 | -type periods() :: [period()]. 51 | 52 | %%%------------------------------------------------------------------------------ 53 | %%% API 54 | %%%------------------------------------------------------------------------------ 55 | 56 | %% add_seconds/1 57 | -spec add_seconds(Seconds :: integer()) -> datetime(). 58 | %% @doc 59 | %% Same as uef_time:add_seconds(erlang:localtime(), Seconds). See docs of uef_time:add_seconds/2. 60 | %% @end 61 | add_seconds(Seconds) -> 62 | add_seconds(erlang:localtime(), Seconds). 63 | 64 | %% add_seconds/2 65 | -spec add_seconds(DateOrDatetime :: date() | datetime(), Seconds :: integer()) -> datetime(). 66 | %% @doc 67 | %% Adds the number of seconds Seconds to DateOrDatetime and returns a new datetime value. 68 | %% @end 69 | add_seconds({_Y, _M, _D} = Date, Seconds) -> 70 | add_seconds({Date, {0, 0, 0}}, Seconds); 71 | add_seconds(DateTime, Seconds) -> 72 | Seconds2 = calendar:datetime_to_gregorian_seconds(DateTime) + Seconds, 73 | calendar:gregorian_seconds_to_datetime(Seconds2). 74 | 75 | 76 | %% add_minutes/1 77 | -spec add_minutes(Minutes :: integer()) -> datetime(). 78 | %% @doc 79 | %% Same as uef_time:add_seconds(Minutes * 60). See docs of uef_time:add_seconds/1. 80 | %% @end 81 | add_minutes(Minutes) -> 82 | add_seconds(Minutes * 60). 83 | 84 | %% add_minutes/2 85 | -spec add_minutes(DateOrDatetime :: date() | datetime(), Minutes :: integer()) -> datetime(). 86 | %% @doc 87 | %% Adds the number of minutes Minutes to DateOrDatetime and returns a new datetime value. 88 | %% @end 89 | add_minutes(DateOrDatetime, Minutes) -> 90 | add_seconds(DateOrDatetime, Minutes * 60). 91 | 92 | 93 | %% add_hours/1 94 | -spec add_hours(Hours :: integer()) -> datetime(). 95 | %% @doc 96 | %% Same as uef_time:add_seconds(Hours * 3600). See docs of uef_time:add_seconds/1. 97 | %% @end 98 | add_hours(Hours) -> 99 | add_seconds(Hours * 3600). 100 | 101 | %% add_hours/2 102 | -spec add_hours(DateOrDatetime :: date() | datetime(), Hours :: integer()) -> datetime(). 103 | %% @doc 104 | %% Adds the number of hours Hours to DateOrDatetime and returns a new datetime value. 105 | %% @end 106 | add_hours(DateOrDatetime, Hours) -> 107 | add_seconds(DateOrDatetime, Hours * 3600). 108 | 109 | 110 | %% add_days/1 111 | -spec add_days(Days :: integer()) -> datetime(). 112 | %% @doc 113 | %% Same as uef_time:add_seconds(Days * 86400). See docs of uef_time:add_seconds/1. 114 | %% @end 115 | add_days(Days) -> 116 | add_seconds(Days * 86400). 117 | 118 | %% add_days/2 119 | -spec add_days(DateOrDatetime :: date() | datetime(), Days :: integer()) -> NewDateOrDateTime :: date() | datetime(). 120 | %% @doc 121 | %% Adds the number of days Days to DateOrDatetime and returns a new date or datetime value. 122 | %% The type of NewDateOrDateTime is the same as the type of DateOrDatetime. 123 | %% @end 124 | add_days(DateOrDatetime, Days) -> 125 | {Date, Time} = add_seconds(DateOrDatetime, Days * 86400), 126 | case DateOrDatetime of 127 | {_, _} -> {Date, Time}; % type datetime() 128 | _ -> Date % type date() 129 | end. 130 | 131 | %% add_weeks/1 132 | -spec add_weeks(Weeks :: integer()) -> datetime(). 133 | %% @doc 134 | %% Same as uef_time:add_seconds(Weeks * 604800). See docs of uef_time:add_seconds/1. 135 | %% @end 136 | add_weeks(Weeks) -> 137 | add_seconds(Weeks * 604800). 138 | 139 | %% add_weeks/1 140 | -spec add_weeks(DateOrDatetime :: date() | datetime(), Weeks :: integer()) -> NewDateOrDateTime :: date() | datetime(). 141 | %% @doc 142 | %% Adds the number of weeks Weeks to DateOrDatetime and returns a new date or datetime value. 143 | %% The type of NewDateOrDateTime is the same as the type of DateOrDatetime. 144 | %% @end 145 | add_weeks(DateOrDatetime, Weeks) -> 146 | add_days(DateOrDatetime, Weeks * 7). 147 | 148 | %% add_months/1 149 | -spec add_months(Months :: integer()) -> datetime(). 150 | %% @doc 151 | %% Same as uef_time:add_months(erlang:localtime(), Months). See docs of uef_time:add_months/2. 152 | %% @end 153 | add_months(Months) -> 154 | add_months(erlang:localtime(), Months). 155 | 156 | %% add_months/2 157 | -spec add_months(DateOrDatetime :: date() | datetime(), Months :: integer()) -> NewDateOrDateTime :: date() | datetime(). 158 | %% @doc 159 | %% Adds the number of months Months to DateOrDatetime and returns a new date or datetime value. 160 | %% The type of NewDateOrDateTime is the same as the type of DateOrDatetime. 161 | %% @end 162 | add_months(DateOrDatetime, Months) -> 163 | ok = validate_datetime(DateOrDatetime), 164 | {Year1, Mon1, Day1, Time} = case DateOrDatetime of 165 | { {Y, M, D}, T} -> {Y, M, D, T}; 166 | {Y, M, D} -> {Y, M, D, skip} 167 | end, 168 | TotalMonths = Year1 * 12 + Mon1 + Months, 169 | {Year2, Mon2} = case TotalMonths rem 12 of 170 | 0 -> {(TotalMonths div 12) - 1, 12}; 171 | N -> {TotalMonths div 12, N} 172 | end, 173 | Date = add_months_check_date(Year2, Mon2, Day1), 174 | case Time of 175 | skip -> Date; 176 | _ -> {Date, Time} 177 | end. 178 | 179 | 180 | %% add_years/1 181 | -spec add_years(Years :: integer()) -> datetime(). 182 | %% @doc 183 | %% Same as uef_time:add_years(erlang:localtime(), Years). See docs of uef_time:add_years/2. 184 | %% @end 185 | add_years(Years) -> 186 | add_years(erlang:localtime(), Years). 187 | 188 | %% add_years/2 189 | -spec add_years(DateOrDatetime :: date() | datetime(), Years :: integer()) -> NewDateOrDateTime :: date() | datetime(). 190 | %% @doc 191 | %% Adds the number of years Years to DateOrDatetime and returns a new date or datetime value. 192 | %% The type of NewDateOrDateTime is the same as the type of DateOrDatetime. 193 | %% @end 194 | add_years(DateOrDatetime, Years) -> 195 | add_months(DateOrDatetime, Years * 12). 196 | 197 | 198 | %% add_time/1 199 | -spec add_time(Periods :: periods()) -> datetime(). 200 | %% @doc 201 | %% Same as uef_time:add_time(erlang:localtime(), Periods). 202 | %% @end 203 | add_time(Periods) -> 204 | add_time(erlang:localtime(), Periods). 205 | 206 | %% add_time/2 207 | -spec add_time(DateOrDatetime :: date() | datetime(), periods()) -> NewDateOrDateTime :: date() | datetime(). 208 | %% @doc 209 | %% Adds one or more periods of time to DateOrDatetime and returns a new date or datetime value. 210 | %% @end 211 | add_time(DT, []) -> 212 | DT; 213 | add_time(DT, [{N, Ptype} | Tail]) when is_integer(N) andalso is_atom(Ptype) -> 214 | DT2 = case Ptype of 215 | % seconds 216 | sec -> add_seconds(DT, N); 217 | second -> add_seconds(DT, N); 218 | seconds -> add_seconds(DT, N); 219 | % minutes 220 | min -> add_minutes(DT, N); 221 | minute -> add_minutes(DT, N); 222 | minutes -> add_minutes(DT, N); 223 | % hours 224 | hrs -> add_hours(DT, N); 225 | hour -> add_hours(DT, N); 226 | hours -> add_hours(DT, N); 227 | % days 228 | day -> add_days(DT, N); 229 | days -> add_days(DT, N); 230 | % weeks 231 | week -> add_weeks(DT, N); 232 | weeks -> add_weeks(DT, N); 233 | % months 234 | month -> add_months(DT, N); 235 | months -> add_months(DT, N); 236 | % years 237 | year -> add_years(DT, N); 238 | years -> add_years(DT, N); 239 | % other 240 | _ -> erlang:error({badarg, Ptype}) 241 | end, 242 | add_time(DT2, Tail); 243 | add_time(DT, [{Ptype, N} | Tail]) when is_atom(Ptype) andalso is_integer(N) -> 244 | add_time(DT, [{N, Ptype} | Tail]); 245 | add_time(_, [Arg | _]) -> 246 | erlang:error({badarg, Arg}). 247 | 248 | %% today/0 249 | -spec today() -> CurrentDate :: date(). 250 | %% @doc 251 | %% Returns the current date as {Year, Month, Day}. Same as erlang:date(). CurrentDate is of type calendar:date(). 252 | %% @end 253 | today() -> 254 | erlang:date(). 255 | 256 | %% tomorrow/0 257 | -spec tomorrow() -> TomorrowDate :: date(). 258 | %% @doc 259 | %% Returns tomorrow's date as {Year, Month, Day}. TomorrowDate is of type calendar:date(). 260 | %% @end 261 | tomorrow() -> 262 | add_days(erlang:date(), 1). 263 | 264 | %% yesterday/0 265 | -spec yesterday() -> YesterdayDate :: date(). 266 | %% @doc 267 | %% Returns yesterday's date as {Year, Month, Day}. YesterdayDate is of type calendar:date(). 268 | %% @end 269 | yesterday() -> 270 | add_days(erlang:date(), -1). 271 | 272 | %% days_diff/1 273 | -spec days_diff(Date :: date()) -> Days :: integer(). 274 | %% @doc 275 | %% Returns the difference in days between Date and the current local date provided by function erlang:date(). 276 | %% Date must be of type calendar:date() ({Year, Month, Day}). 277 | %% Days is a positive value if Date is after erlang:date() or a negative value otherwise. 278 | %% @end 279 | days_diff(Date) -> 280 | days_diff(erlang:date(), Date). 281 | 282 | %% days_diff/2 283 | -spec days_diff(Date1 :: date(), Date2 :: date()) -> Days :: integer(). 284 | %% @doc 285 | %% Returns the difference in days between Date2 and Date1. 286 | %% Date1 and Date2 must be of type calendar:date() ({Year, Month, Day}). 287 | %% Days is a positive value if Date2 is after Date1 or a negative value otherwise. 288 | %% @end 289 | days_diff(Date1, Date2) -> 290 | calendar:date_to_gregorian_days(Date2) - calendar:date_to_gregorian_days(Date1). 291 | 292 | %% seconds_diff/1 293 | -spec seconds_diff(DateTime :: datetime()) -> Seconds :: integer(). 294 | %% @doc 295 | %% Returns the difference in seconds between Date and the current local time provided by function erlang:localtime(). 296 | %% DateTime must be of type calendar:datetime() ({{Year, Month, Day}, {Hour, Minute, Second}}). 297 | %% Seconds is a positive value if DateTime is after erlang:localtime() or a negative value otherwise. 298 | %% @end 299 | seconds_diff(DateTime) -> 300 | DateTimeNow = erlang:localtime(), 301 | calendar:datetime_to_gregorian_seconds(DateTime) - calendar:datetime_to_gregorian_seconds(DateTimeNow). 302 | 303 | %% seconds_diff/2 304 | -spec seconds_diff(DateTime1 :: datetime(), DateTime2 :: datetime()) -> integer(). 305 | %% @doc 306 | %% Returns the difference in seconds between DateTime2 and DateTime1. 307 | %% DateTime1 and DateTime2 must be of type calendar:datetime() ({{Year, Month, Day}, {Hour, Minute, Second}}). 308 | %% Seconds is a positive value if DateTime2 is after DateTime1 or a negative value otherwise. 309 | %% @end 310 | seconds_diff(DateTime1, DateTime2) -> 311 | calendar:datetime_to_gregorian_seconds(DateTime2) - calendar:datetime_to_gregorian_seconds(DateTime1). 312 | 313 | 314 | %% unix_time/0 315 | -spec unix_time() -> Seconds :: integer(). 316 | %% @doc 317 | %% Returns the current number of seconds since 00:00:00 (UTC), 1 January 1970. It also known as Unix time or POSIX time or UNIX Epoch time. 318 | %% @end 319 | unix_time() -> 320 | erlang:system_time(seconds). 321 | 322 | %% unix_time/1 323 | -spec unix_time(Datetime :: datetime()) -> Seconds :: integer(). 324 | %% @doc 325 | %% Returns the number of seconds elapsed between 00:00:00 (UTC), 1 January 1970 and Datetime. Datetime must be of type calenadr:datetime(). 326 | %% @end 327 | unix_time(Datetime) -> 328 | calendar:datetime_to_gregorian_seconds(Datetime) - ?UNIX_EPOCH_GREGORIAN_SECONDS. 329 | 330 | 331 | %%%------------------------------------------------------------------------------ 332 | %%% Internal functions 333 | %%%------------------------------------------------------------------------------ 334 | 335 | %% validate_datetime/1 336 | -spec validate_datetime(date() | datetime()) -> ok | no_return(). 337 | validate_datetime({_, _, _} = Date) -> 338 | validate_datetime({Date, {0, 0, 0}}); 339 | validate_datetime({Date, {Hour, Min, Sec}}) -> 340 | true = calendar:valid_date(Date), 341 | true = erlang:is_integer(Hour) andalso Hour > -1 andalso Hour < 24, 342 | true = erlang:is_integer(Min) andalso Min > -1 andalso Min < 60, 343 | true = erlang:is_integer(Sec) andalso Sec > -1 andalso Sec < 60, 344 | ok; 345 | validate_datetime(Other) -> 346 | erlang:error({badarg, Other}). 347 | 348 | 349 | %% add_months_check_date/3 350 | -spec add_months_check_date(integer(), integer(), integer()) -> date() | no_return(). 351 | add_months_check_date(Y, M, D) when D > 0 -> 352 | case calendar:valid_date(Y, M, D) of 353 | true -> 354 | {Y, M, D}; 355 | false -> 356 | add_months_check_date(Y, M, D - 1) 357 | end; 358 | add_months_check_date(Y, M, D) -> % just in case 359 | erlang:error({out_of_range, {Y, M, D}}). 360 | -------------------------------------------------------------------------------- /test/data/uef_file_1.txt: -------------------------------------------------------------------------------- 1 | test1 2 | -------------------------------------------------------------------------------- /test/uef_bin_tests.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2019-2024, Sergei Semichev . All Rights Reserved. 2 | %% 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | 15 | -module(uef_bin_tests). 16 | 17 | -include_lib("eunit/include/eunit.hrl"). 18 | 19 | %%%------------------------------------------------------------------------------ 20 | %%% Test functions 21 | %%%------------------------------------------------------------------------------ 22 | 23 | numeric_prefix_test_() -> 24 | [ 25 | ?_assertEqual(<<>>, uef_bin:numeric_prefix(<<"a234234">>)), 26 | ?_assertEqual(<<"123">>, uef_bin:numeric_prefix(<<"123a456">>)), 27 | ?_assertEqual(<<"123">>, uef_bin:numeric_prefix(<<"123">>)), 28 | ?_assertEqual(<<"0">>, uef_bin:numeric_prefix(<<"0test">>)), 29 | ?_assertEqual(<<"1">>, uef_bin:numeric_prefix(<<"1test">>)), 30 | ?_assertEqual(<<"2">>, uef_bin:numeric_prefix(<<"2test">>)), 31 | ?_assertEqual(<<"3">>, uef_bin:numeric_prefix(<<"3test">>)), 32 | ?_assertEqual(<<"4">>, uef_bin:numeric_prefix(<<"4test">>)), 33 | ?_assertEqual(<<"5">>, uef_bin:numeric_prefix(<<"5test">>)), 34 | ?_assertEqual(<<"6">>, uef_bin:numeric_prefix(<<"6test">>)), 35 | ?_assertEqual(<<"7">>, uef_bin:numeric_prefix(<<"7test">>)), 36 | ?_assertEqual(<<"8">>, uef_bin:numeric_prefix(<<"8test">>)), 37 | ?_assertEqual(<<"9">>, uef_bin:numeric_prefix(<<"9test">>)) 38 | ]. 39 | 40 | binary_join_test_() -> 41 | [ 42 | ?_assertEqual(<<>>, uef_bin:binary_join([], <<"any">>)), 43 | ?_assertEqual(<<>>, uef_bin:binary_join([], <<>>)), 44 | ?_assertEqual(<<"www.example.com">>, uef_bin:binary_join([<<"www">>, <<"example">>, <<"com">>], <<".">>)), 45 | ?_assertEqual(<<"www">>, uef_bin:binary_join([<<"www">>], <<".">>)) 46 | ]. 47 | 48 | split_test_() -> 49 | [ 50 | ?_assertEqual([], uef_bin:split(<<>>, <<".">>)), 51 | ?_assertEqual([], uef_bin:split(<<>>, <<>>)), 52 | ?_assertEqual([<<".www.example.com.">>], uef_bin:split(<<".www.example.com.">>, <<>>, trim_all)), 53 | ?_assertEqual([<<>>,<<"www">>,<<"example">>,<<"com">>,<<>>], uef_bin:split(<<".www.example.com.">>, <<".">>)), 54 | ?_assertEqual([<<"www">>,<<"example">>,<<"com">>], uef_bin:split(<<"www.example.com">>, <<".">>)), 55 | ?_assertEqual([<<"www.example.com">>], uef_bin:split(<<"www.example.com">>, <<"A">>)), 56 | ?_assertEqual([<<"www">>,<<"example">>,<<"com">>], uef_bin:split(<<".....www.example.com....">>, <<".">>, trim_all)) 57 | ]. 58 | 59 | repeat_test_() -> 60 | [ 61 | ?_assertEqual(<<"0">>, uef_bin:repeat(<<"0">>, 1)), 62 | ?_assertEqual(<<"aaaaa">>, uef_bin:repeat(<<"a">>, 5)), 63 | ?_assertEqual(<<0>>, uef_bin:repeat(<<0>>, 1)), 64 | ?_assertEqual(<<0,0,0>>, uef_bin:repeat(<<0>>, 3)), 65 | ?_assertEqual(<<1,1,1,1>>, uef_bin:repeat(<<1,1>>, 2)), 66 | ?_assertEqual(<<1,0,1,0>>, uef_bin:repeat(<<1,0>>, 2)), 67 | ?_assertEqual(<<"abcabcabc">>, uef_bin:repeat(<<"abc">>, 3)), 68 | ?_assertEqual(<<"ЖЖЖ"/utf8>>, uef_bin:repeat(<<"Ж"/utf8>>, 3)) 69 | ]. 70 | 71 | replace_test_() -> 72 | [ 73 | ?_assertEqual(<<>>, uef_bin:replace(<<>>, <<"aa">>, <<"bb">>)), 74 | ?_assertEqual(<<"bbb">>, uef_bin:replace(<<"bbb">>, <<>>, <<"b">>)), 75 | ?_assertEqual(<<"aZZdefgZZ">>, uef_bin:replace(<<"abcdefgbc">>, <<"bc">>, <<"ZZ">>)), 76 | ?_assertEqual(<<"abcZZefgbc">>, uef_bin:replace(<<"abcdefgbc">>, <<"d">>, <<"ZZ">>)) 77 | ]. 78 | 79 | replace_chars_test_() -> 80 | [ 81 | ?_assertEqual(<<"bbb">>, uef_bin:replace_chars(<<"bbb">>, [], <<>>)), 82 | ?_assertEqual(<<"wwwexamplecom">>, uef_bin:replace_chars(<<"..www.example.com.">>, [<<".">>], <<>>)), 83 | ?_assertEqual(<<"examplecom">>, uef_bin:replace_chars(<<"..www.example.com.">>, [<<".">>, <<"w">>], <<>>)) 84 | ]. 85 | 86 | reverse_test_() -> 87 | [ 88 | ?_assertEqual(<<5,4,3,2,1>>, uef_bin:reverse(<<1,2,3,4,5>>)), 89 | ?_assertEqual(<<"HGFEDCBA">>, uef_bin:reverse(<<"ABCDEFGH">>)), 90 | ?_assertEqual(<<>>, uef_bin:reverse(<<>>)), 91 | ?_assertEqual(<<0>>, uef_bin:reverse(<<0>>)), 92 | ?_assertEqual(<<"0">>, uef_bin:reverse(<<"0">>)), 93 | ?_assertEqual(<<1>>, uef_bin:reverse(<<1>>)), 94 | ?_assertEqual(<<"1">>, uef_bin:reverse(<<"1">>)), 95 | ?_assertEqual(<<0, 0, 0>>, uef_bin:reverse(<<0, 0, 0>>)), 96 | ?_assertEqual(<<"ВБА">>, uef_bin:reverse(<<"АБВ">>)) 97 | ]. 98 | 99 | reverse_utf8_test_() -> 100 | [ 101 | ?_assertEqual(<<5,4,3,2,1,0>>, uef_bin:reverse_utf8(<<0,1,2,3,4,5>>)), 102 | ?_assertEqual(<<"543210">>, uef_bin:reverse_utf8(<<"012345">>)), 103 | ?_assertEqual(<<"HGFEDCBA">>, uef_bin:reverse_utf8(<<"ABCDEFGH">>)), 104 | ?_assertEqual(<<>>, uef_bin:reverse_utf8(<<>>)), 105 | ?_assertEqual(<<0>>, uef_bin:reverse_utf8(<<0>>)), 106 | ?_assertEqual(<<"0">>, uef_bin:reverse_utf8(<<"0">>)), 107 | ?_assertEqual(<<1>>, uef_bin:reverse_utf8(<<1>>)), 108 | ?_assertEqual(<<"1">>, uef_bin:reverse_utf8(<<"1">>)), 109 | ?_assertEqual(<<0, 0, 0>>, uef_bin:reverse_utf8(<<0, 0, 0>>)), 110 | ?_assertEqual(<<"ВБА">>, uef_bin:reverse_utf8(<<"АБВ">>)), 111 | ?_assertEqual(<<"ЖЁЕДГВБА"/utf8>>, uef_bin:reverse_utf8(<<"АБВГДЕЁЖ"/utf8>>)), 112 | ?_assertEqual(<<7, 6, 5, 4, "ЖЁЕДГВБА"/utf8, 3, 2, 1>>, uef_bin:reverse_utf8(<<1, 2, 3, "АБВГДЕЁЖ"/utf8, 4, 5, 6, 7>>)), 113 | ?_assertEqual(<<"eßartS eid"/utf8>>, uef_bin:reverse_utf8(<<"die Straße"/utf8>>)), 114 | ?_assertEqual(<<"街條這"/utf8>>, uef_bin:reverse_utf8(<<"這條街"/utf8>>)), 115 | ?_assertEqual(<<"好你"/utf8>>, uef_bin:reverse_utf8(<<"你好"/utf8>>)), 116 | ?_assertEqual(<<"り通"/utf8>>, uef_bin:reverse_utf8(<<"通り"/utf8>>)), 117 | ?_assertEqual(<<"はちにんこ"/utf8>>, uef_bin:reverse_utf8(<<"こんにちは"/utf8>>)) 118 | ]. 119 | 120 | random_latin_binary_test_() -> 121 | Length = 11, 122 | RandomLower = uef_bin:random_latin_binary(Length, lower), 123 | RandomUpper = uef_bin:random_latin_binary(Length, upper), 124 | RandomAny = uef_bin:random_latin_binary(Length, any), 125 | [ 126 | ?_assert(erlang:is_integer(Length) andalso Length > 0), 127 | ?_assertEqual(Length, erlang:byte_size(RandomLower)), 128 | ?_assertEqual(Length, erlang:byte_size(RandomUpper)), 129 | ?_assertEqual(Length, erlang:byte_size(RandomAny)), 130 | ?_assertEqual(ok, validate_random_latin_binary(RandomLower, lower)), 131 | ?_assertEqual(ok, validate_random_latin_binary(RandomUpper, upper)), 132 | ?_assertEqual(ok, validate_random_latin_binary(RandomAny, any)) 133 | ]. 134 | 135 | 136 | strip_left_test_() -> 137 | [ 138 | ?_assertEqual(<<>>, uef_bin:strip_left(<<>>, <<"any">>)), 139 | ?_assertEqual(<<"test">>, uef_bin:strip_left(<<"test">>, <<>>)), 140 | ?_assertEqual(<<"est">>, uef_bin:strip_left(<<"ttest">>, <<"t">>)), 141 | ?_assertEqual(<<"est">>, uef_bin:strip_left(<<"ttest">>, <<"tt">>)), 142 | ?_assertEqual(<<"test">>, uef_bin:strip_left(<<"tttest">>, <<"tt">>)), 143 | ?_assertEqual(<<"est">>, uef_bin:strip_left(<<"ttest">>, $t)), 144 | ?_assertEqual(<<"est">>, uef_bin:strip_left(<<"tttest">>, $t)), 145 | 146 | ?_assertEqual(<<"aa">>, uef_bin:strip_left(<<"aa">>, <<"aaa">>)), 147 | 148 | ?_assertEqual(<<>>, uef_bin:strip_left(<<"aaaaaa">>, $a)), 149 | ?_assertEqual(<<>>, uef_bin:strip_left(<<"aaaaaa">>, <<"a">>)), 150 | 151 | ?_assertEqual(<<"st">>, uef_bin:strip_left(<<"test">>, <<"te">>)), 152 | ?_assertEqual(<<"st">>, uef_bin:strip_left(<<"tetest">>, <<"te">>)), 153 | 154 | ?_assertEqual(<<2,3,4,5>>, uef_bin:strip_left(<<1,1,1,2,3,4,5>>, <<1>>)), 155 | ?_assertEqual(<<2,3,4,5>>, uef_bin:strip_left(<<1,1,1,2,3,4,5>>, 1)), 156 | ?_assertEqual(<<1,2,3,4,5>>, uef_bin:strip_left(<<1,1,1,2,3,4,5>>, <<1,1>>)), 157 | 158 | ?_assertEqual(<<>>, uef_bin:strip_left(<<10, 10, 10, 10>>, 10)), 159 | ?_assertEqual(<<>>, uef_bin:strip_left(<<10, 10, 10, 10>>, <<10>>)), 160 | 161 | ?_assertEqual(<<"ривет"/utf8>>, uef_bin:strip_left(<<"привет"/utf8>>, <<"п"/utf8>>)), 162 | ?_assertEqual(<<"ривет"/utf8>>, uef_bin:strip_left(<<"пппривет"/utf8>>, <<"п"/utf8>>)), 163 | ?_assertEqual(<<"ивет"/utf8>>, uef_bin:strip_left(<<"привет"/utf8>>, <<"пр"/utf8>>)) 164 | ]. 165 | 166 | 167 | strip_right_test_() -> 168 | [ 169 | ?_assertEqual(<<>>, uef_bin:strip_right(<<>>, <<"any">>)), 170 | ?_assertEqual(<<"test">>, uef_bin:strip_right(<<"test">>, <<>>)), 171 | ?_assertEqual(<<"tes">>, uef_bin:strip_right(<<"testtt">>, <<"t">>)), 172 | ?_assertEqual(<<"test">>, uef_bin:strip_right(<<"testtt">>, <<"tt">>)), 173 | ?_assertEqual(<<"test">>, uef_bin:strip_right(<<"testtttt">>, <<"tt">>)), 174 | ?_assertEqual(<<"tes">>, uef_bin:strip_right(<<"testtt">>, $t)), 175 | 176 | ?_assertEqual(<<"aa">>, uef_bin:strip_right(<<"aa">>, <<"aaa">>)), 177 | 178 | ?_assertEqual(<<>>, uef_bin:strip_right(<<"aaaaaa">>, $a)), 179 | ?_assertEqual(<<>>, uef_bin:strip_right(<<"aaaaaa">>, <<"a">>)), 180 | 181 | ?_assertEqual(<<"te">>, uef_bin:strip_right(<<"test">>, <<"st">>)), 182 | ?_assertEqual(<<"t">>, uef_bin:strip_right(<<"test">>, <<"est">>)), 183 | ?_assertEqual(<<>>, uef_bin:strip_right(<<"test">>, <<"test">>)), 184 | 185 | ?_assertEqual(<<1,2,3,4>>, uef_bin:strip_right(<<1,2,3,4,5,5,5>>, <<5>>)), 186 | ?_assertEqual(<<1,2,3,4>>, uef_bin:strip_right(<<1,2,3,4,5,5,5>>, 5)), 187 | ?_assertEqual(<<1,2,3,4,5>>, uef_bin:strip_right(<<1,2,3,4,5,5,5>>, <<5,5>>)), 188 | 189 | ?_assertEqual(<<>>, uef_bin:strip_right(<<10, 10, 10, 10>>, 10)), 190 | ?_assertEqual(<<>>, uef_bin:strip_right(<<10, 10, 10, 10>>, <<10>>)), 191 | 192 | ?_assertEqual(<<"приве"/utf8>>, uef_bin:strip_right(<<"привет"/utf8>>, <<"т"/utf8>>)), 193 | ?_assertEqual(<<"приве"/utf8>>, uef_bin:strip_right(<<"приветттт"/utf8>>, <<"т"/utf8>>)), 194 | ?_assertEqual(<<"привет"/utf8>>, uef_bin:strip_right(<<"приветтттт"/utf8>>, <<"тт"/utf8>>)) 195 | ]. 196 | 197 | 198 | strip_both_test_() -> 199 | [ 200 | ?_assertEqual(<<>>, uef_bin:strip_both(<<>>, <<"any">>)), 201 | ?_assertEqual(<<"test">>, uef_bin:strip_both(<<"test">>, <<>>)), 202 | 203 | ?_assertEqual(<<"es">>, uef_bin:strip_both(<<"tttest">>, <<"t">>)), 204 | ?_assertEqual(<<"est">>, uef_bin:strip_both(<<"ttest">>, <<"tt">>)), 205 | ?_assertEqual(<<"test">>, uef_bin:strip_both(<<"tttest">>, <<"tt">>)), 206 | ?_assertEqual(<<"test">>, uef_bin:strip_both(<<"tttesttt">>, <<"tt">>)), 207 | ?_assertEqual(<<"es">>, uef_bin:strip_both(<<"ttest">>, $t)), 208 | ?_assertEqual(<<"es">>, uef_bin:strip_both(<<"tttesttt">>, $t)), 209 | 210 | ?_assertEqual(<<"aa">>, uef_bin:strip_both(<<"aa">>, <<"aaa">>)), 211 | 212 | ?_assertEqual(<<>>, uef_bin:strip_both(<<"aaaaaa">>, $a)), 213 | ?_assertEqual(<<>>, uef_bin:strip_both(<<"aaaaaa">>, <<"a">>)), 214 | 215 | ?_assertEqual(<<"st">>, uef_bin:strip_both(<<"test">>, <<"te">>)), 216 | ?_assertEqual(<<"st">>, uef_bin:strip_both(<<"tetest">>, <<"te">>)), 217 | 218 | ?_assertEqual(<<"te">>, uef_bin:strip_both(<<"test">>, <<"st">>)), 219 | ?_assertEqual(<<"te">>, uef_bin:strip_both(<<"testst">>, <<"st">>)), 220 | 221 | ?_assertEqual(<<2,3,4,5>>, uef_bin:strip_both(<<1,1,1,2,3,4,5>>, <<1>>)), 222 | ?_assertEqual(<<2,3,4,5>>, uef_bin:strip_both(<<1,1,1,2,3,4,5>>, 1)), 223 | ?_assertEqual(<<1,2,3,4,5>>, uef_bin:strip_both(<<1,1,1,2,3,4,5>>, <<1,1>>)), 224 | 225 | ?_assertEqual(<<>>, uef_bin:strip_both(<<10, 10, 10, 10>>, 10)), 226 | ?_assertEqual(<<>>, uef_bin:strip_both(<<10, 10, 10, 10>>, <<10>>)), 227 | 228 | ?_assertEqual(<<2,3,4,5>>, uef_bin:strip_both(<<1,1,1,2,3,4,5,1,1,1>>, <<1>>)), 229 | ?_assertEqual(<<2,3,4,5>>, uef_bin:strip_both(<<1,1,1,2,3,4,5,1,1,1>>, 1)), 230 | 231 | ?_assertEqual(<<1,2,3,4>>, uef_bin:strip_both(<<1,2,3,4,5,5,5>>, <<5>>)), 232 | ?_assertEqual(<<1,2,3,4>>, uef_bin:strip_both(<<1,2,3,4,5,5,5>>, 5)), 233 | ?_assertEqual(<<1,2,3,4,5>>, uef_bin:strip_both(<<1,2,3,4,5,5,5>>, <<5,5>>)), 234 | 235 | ?_assertEqual(<<>>, uef_bin:strip_both(<<10, 10, 10, 10>>, 10)), 236 | ?_assertEqual(<<>>, uef_bin:strip_both(<<10, 10, 10, 10>>, <<10>>)), 237 | 238 | ?_assertEqual(<<"ривет"/utf8>>, uef_bin:strip_both(<<"привет"/utf8>>, <<"п"/utf8>>)), 239 | ?_assertEqual(<<"ривет"/utf8>>, uef_bin:strip_both(<<"пппривет"/utf8>>, <<"п"/utf8>>)), 240 | ?_assertEqual(<<"ивет"/utf8>>, uef_bin:strip_both(<<"привет"/utf8>>, <<"пр"/utf8>>)), 241 | 242 | ?_assertEqual(<<"приве"/utf8>>, uef_bin:strip_both(<<"привет"/utf8>>, <<"т"/utf8>>)), 243 | ?_assertEqual(<<"приве"/utf8>>, uef_bin:strip_both(<<"приветттт"/utf8>>, <<"т"/utf8>>)), 244 | ?_assertEqual(<<"привет"/utf8>>, uef_bin:strip_both(<<"приветтттт"/utf8>>, <<"тт"/utf8>>)), 245 | ?_assertEqual(<<"привет"/utf8>>, uef_bin:strip_both(<<"абабабприветабабаб"/utf8>>, <<"аб"/utf8>>)), 246 | 247 | ?_assertEqual(<<"привет"/utf8>>, uef_bin:strip_both(<<"жжжжжприветжжжжж"/utf8>>, <<"ж"/utf8>>)), 248 | ?_assertEqual(<<"жприветж"/utf8>>, uef_bin:strip_both(<<"жжжжжприветжжжжж"/utf8>>, <<"жж"/utf8>>)) 249 | ]. 250 | 251 | 252 | chomp_test_() -> 253 | [ 254 | ?_assertEqual(<<>>, uef_bin:chomp(<<>>)), 255 | ?_assertEqual(<<>>, uef_bin:chomp(<<"\n">>)), 256 | ?_assertEqual(<<>>, uef_bin:chomp(<<"\r">>)), 257 | ?_assertEqual(<<>>, uef_bin:chomp(<<"\n\n">>)), 258 | ?_assertEqual(<<>>, uef_bin:chomp(<<"\r\r">>)), 259 | ?_assertEqual(<<>>, uef_bin:chomp(<<"\r\n\r\n">>)), 260 | 261 | ?_assertEqual(<<"aaa">>, uef_bin:chomp(<<"aaa\n">>)), 262 | ?_assertEqual(<<"aaa">>, uef_bin:chomp(<<"aaa\r">>)), 263 | ?_assertEqual(<<"aaa">>, uef_bin:chomp(<<"aaa\n\n\n">>)), 264 | ?_assertEqual(<<"aaa">>, uef_bin:chomp(<<"aaa\r\r\r">>)), 265 | ?_assertEqual(<<"aaa">>, uef_bin:chomp(<<"aaa\r\n\r">>)), 266 | ?_assertEqual(<<"aaa">>, uef_bin:chomp(<<"aaa\n\r\n">>)), 267 | ?_assertEqual(<<"aaa">>, uef_bin:chomp(<<"aaa\r\n\r\n">>)), 268 | 269 | ?_assertEqual(<<"\naaa">>, uef_bin:chomp(<<"\naaa\n">>)), 270 | ?_assertEqual(<<"\raaa">>, uef_bin:chomp(<<"\raaa\r">>)), 271 | ?_assertEqual(<<"\n\n\naaa">>, uef_bin:chomp(<<"\n\n\naaa\n\n\n">>)), 272 | ?_assertEqual(<<"\r\r\raaa">>, uef_bin:chomp(<<"\r\r\raaa\r\r\r">>)), 273 | ?_assertEqual(<<"\r\n\raaa">>, uef_bin:chomp(<<"\r\n\raaa\r\n\r">>)), 274 | ?_assertEqual(<<"\n\r\naaa">>, uef_bin:chomp(<<"\n\r\naaa\n\r\n">>)), 275 | ?_assertEqual(<<"\r\n\r\naaa">>, uef_bin:chomp(<<"\r\n\r\naaa\r\n\r\n">>)) 276 | ]. 277 | 278 | 279 | %%%------------------------------------------------------------------------------ 280 | %%% Internal functions 281 | %%%------------------------------------------------------------------------------ 282 | validate_random_latin_binary(Bin, CaseFlag) -> 283 | case Bin of 284 | <<>> -> 285 | ok; 286 | <> -> 287 | IsInRange = case CaseFlag of 288 | lower -> 289 | (C >= $0 andalso C =< $9) orelse (C >= $a andalso C =< $z); 290 | upper -> 291 | (C >= $0 andalso C =< $9) orelse (C >= $A andalso C =< $Z); 292 | any -> 293 | (C >= $0 andalso C =< $9) orelse (C >= $a andalso C =< $z) orelse (C >= $A andalso C =< $Z) 294 | end, 295 | case IsInRange of 296 | true -> 297 | validate_random_latin_binary(Rest, CaseFlag); 298 | false -> 299 | {error, {invalid_char, C}} 300 | end; 301 | 302 | _ -> 303 | {error, other} 304 | end. 305 | -------------------------------------------------------------------------------- /test/uef_crypt_tests.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2019-2024, Sergei Semichev . All Rights Reserved. 2 | %% 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | 15 | -module(uef_crypt_tests). 16 | 17 | -include_lib("eunit/include/eunit.hrl"). 18 | 19 | %%%------------------------------------------------------------------------------ 20 | %%% Tests 21 | %%%------------------------------------------------------------------------------ 22 | 23 | md5_hex_test_() -> 24 | % Check with https://www.md5hashgenerator.com/ 25 | [ 26 | ?_assertEqual(<<"e2fc714c4727ee9395f324cd2e7f331f">>, uef_crypt:md5_hex(<<"abcd">>)), 27 | ?_assertEqual(<<"e2fc714c4727ee9395f324cd2e7f331f">>, uef_crypt:md5_hex("abcd")), 28 | ?_assertEqual(<<"e2fc714c4727ee9395f324cd2e7f331f">>, uef_crypt:md5_hex(["a", "bcd"])), 29 | ?_assertEqual(<<"e2fc714c4727ee9395f324cd2e7f331f">>, uef_crypt:md5_hex(["a", "b", ["c", ["d"]]])), 30 | ?_assertEqual(<<"6cd3556deb0da54bca060b4c39479839">>, uef_crypt:md5_hex(<<"Hello, world!">>)), 31 | ?_assertEqual(<<"aa3ad0839299f15e1e407d0d1a62f507">>, uef_crypt:md5_hex(<<"кто здесь?"/utf8>>)), 32 | ?_assertEqual(<<"56bf18da956bee82e008090e3d95ad7c">>, uef_crypt:md5_hex(<<"誰在這裡"/utf8>>)) 33 | ]. 34 | -------------------------------------------------------------------------------- /test/uef_encode_tests.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2019-2024, Sergei Semichev . All Rights Reserved. 2 | %% 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | 15 | -module(uef_encode_tests). 16 | 17 | -include_lib("eunit/include/eunit.hrl"). 18 | 19 | %%%------------------------------------------------------------------------------ 20 | %%% Tests 21 | %%%------------------------------------------------------------------------------ 22 | 23 | html_encode_bin_test_() -> 24 | [ 25 | ?_assertEqual(<<"<>&©
™">>, uef_encode:html_encode_bin("<>&©\n™")), 26 | ?_assertEqual(<<"♦±Σ">>, uef_encode:html_encode_bin("♦±Σ")) 27 | ]. 28 | 29 | html_encode_list_test_() -> 30 | [ 31 | ?_assertEqual([<<"<">>,<<">">>,<<"&">>,<<"©">>,<<"
">>,<<"™">>], uef_encode:html_encode_list("<>&©\n™")), 32 | ?_assertEqual([<<"♦">>,<<"±">>,<<"Σ">>], uef_encode:html_encode_list("♦±Σ")) 33 | ]. 34 | 35 | win_to_utf8_test_() -> 36 | [ 37 | ?_assertEqual(<<16#0000/utf8>>, uef_encode:win_to_utf8(<<16#00>>)), %% NULL 38 | ?_assertEqual(<<16#0001/utf8>>, uef_encode:win_to_utf8(<<16#01>>)), %% START OF HEADING 39 | ?_assertEqual(<<16#0002/utf8>>, uef_encode:win_to_utf8(<<16#02>>)), %% START OF TEXT 40 | ?_assertEqual(<<16#0003/utf8>>, uef_encode:win_to_utf8(<<16#03>>)), %% END OF TEXT 41 | ?_assertEqual(<<16#0004/utf8>>, uef_encode:win_to_utf8(<<16#04>>)), %% END OF TRANSMISSION 42 | ?_assertEqual(<<16#0005/utf8>>, uef_encode:win_to_utf8(<<16#05>>)), %% ENQUIRY 43 | ?_assertEqual(<<16#0006/utf8>>, uef_encode:win_to_utf8(<<16#06>>)), %% ACKNOWLEDGE 44 | ?_assertEqual(<<16#0007/utf8>>, uef_encode:win_to_utf8(<<16#07>>)), %% BELL 45 | ?_assertEqual(<<16#0008/utf8>>, uef_encode:win_to_utf8(<<16#08>>)), %% BACKSPACE 46 | ?_assertEqual(<<16#0009/utf8>>, uef_encode:win_to_utf8(<<16#09>>)), %% HORIZONTAL TABULATION 47 | ?_assertEqual(<<16#000A/utf8>>, uef_encode:win_to_utf8(<<16#0A>>)), %% LINE FEED 48 | ?_assertEqual(<<16#000B/utf8>>, uef_encode:win_to_utf8(<<16#0B>>)), %% VERTICAL TABULATION 49 | ?_assertEqual(<<16#000C/utf8>>, uef_encode:win_to_utf8(<<16#0C>>)), %% FORM FEED 50 | ?_assertEqual(<<16#000D/utf8>>, uef_encode:win_to_utf8(<<16#0D>>)), %% CARRIAGE RETURN 51 | ?_assertEqual(<<16#000E/utf8>>, uef_encode:win_to_utf8(<<16#0E>>)), %% SHIFT OUT 52 | ?_assertEqual(<<16#000F/utf8>>, uef_encode:win_to_utf8(<<16#0F>>)), %% SHIFT IN 53 | ?_assertEqual(<<16#0010/utf8>>, uef_encode:win_to_utf8(<<16#10>>)), %% DATA LINK ESCAPE 54 | ?_assertEqual(<<16#0011/utf8>>, uef_encode:win_to_utf8(<<16#11>>)), %% DEVICE CONTROL ONE 55 | ?_assertEqual(<<16#0012/utf8>>, uef_encode:win_to_utf8(<<16#12>>)), %% DEVICE CONTROL TWO 56 | ?_assertEqual(<<16#0013/utf8>>, uef_encode:win_to_utf8(<<16#13>>)), %% DEVICE CONTROL THREE 57 | ?_assertEqual(<<16#0014/utf8>>, uef_encode:win_to_utf8(<<16#14>>)), %% DEVICE CONTROL FOUR 58 | ?_assertEqual(<<16#0015/utf8>>, uef_encode:win_to_utf8(<<16#15>>)), %% NEGATIVE ACKNOWLEDGE 59 | ?_assertEqual(<<16#0016/utf8>>, uef_encode:win_to_utf8(<<16#16>>)), %% SYNCHRONOUS IDLE 60 | ?_assertEqual(<<16#0017/utf8>>, uef_encode:win_to_utf8(<<16#17>>)), %% END OF TRANSMISSION BLOCK 61 | ?_assertEqual(<<16#0018/utf8>>, uef_encode:win_to_utf8(<<16#18>>)), %% CANCEL 62 | ?_assertEqual(<<16#0019/utf8>>, uef_encode:win_to_utf8(<<16#19>>)), %% END OF MEDIUM 63 | ?_assertEqual(<<16#001A/utf8>>, uef_encode:win_to_utf8(<<16#1A>>)), %% SUBSTITUTE 64 | ?_assertEqual(<<16#001B/utf8>>, uef_encode:win_to_utf8(<<16#1B>>)), %% ESCAPE 65 | ?_assertEqual(<<16#001C/utf8>>, uef_encode:win_to_utf8(<<16#1C>>)), %% FILE SEPARATOR 66 | ?_assertEqual(<<16#001D/utf8>>, uef_encode:win_to_utf8(<<16#1D>>)), %% GROUP SEPARATOR 67 | ?_assertEqual(<<16#001E/utf8>>, uef_encode:win_to_utf8(<<16#1E>>)), %% RECORD SEPARATOR 68 | ?_assertEqual(<<16#001F/utf8>>, uef_encode:win_to_utf8(<<16#1F>>)), %% UNIT SEPARATOR 69 | ?_assertEqual(<<16#0020/utf8>>, uef_encode:win_to_utf8(<<16#20>>)), %% SPACE 70 | ?_assertEqual(<<16#0021/utf8>>, uef_encode:win_to_utf8(<<16#21>>)), %% EXCLAMATION MARK 71 | ?_assertEqual(<<16#0022/utf8>>, uef_encode:win_to_utf8(<<16#22>>)), %% QUOTATION MARK 72 | ?_assertEqual(<<16#0023/utf8>>, uef_encode:win_to_utf8(<<16#23>>)), %% NUMBER SIGN 73 | ?_assertEqual(<<16#0024/utf8>>, uef_encode:win_to_utf8(<<16#24>>)), %% DOLLAR SIGN 74 | ?_assertEqual(<<16#0025/utf8>>, uef_encode:win_to_utf8(<<16#25>>)), %% PERCENT SIGN 75 | ?_assertEqual(<<16#0026/utf8>>, uef_encode:win_to_utf8(<<16#26>>)), %% AMPERSAND 76 | ?_assertEqual(<<16#0027/utf8>>, uef_encode:win_to_utf8(<<16#27>>)), %% APOSTROPHE 77 | ?_assertEqual(<<16#0028/utf8>>, uef_encode:win_to_utf8(<<16#28>>)), %% LEFT PARENTHESIS 78 | ?_assertEqual(<<16#0029/utf8>>, uef_encode:win_to_utf8(<<16#29>>)), %% RIGHT PARENTHESIS 79 | ?_assertEqual(<<16#002A/utf8>>, uef_encode:win_to_utf8(<<16#2A>>)), %% ASTERISK 80 | ?_assertEqual(<<16#002B/utf8>>, uef_encode:win_to_utf8(<<16#2B>>)), %% PLUS SIGN 81 | ?_assertEqual(<<16#002C/utf8>>, uef_encode:win_to_utf8(<<16#2C>>)), %% COMMA 82 | ?_assertEqual(<<16#002D/utf8>>, uef_encode:win_to_utf8(<<16#2D>>)), %% HYPHEN-MINUS 83 | ?_assertEqual(<<16#002E/utf8>>, uef_encode:win_to_utf8(<<16#2E>>)), %% FULL STOP 84 | ?_assertEqual(<<16#002F/utf8>>, uef_encode:win_to_utf8(<<16#2F>>)), %% SOLIDUS 85 | ?_assertEqual(<<16#0030/utf8>>, uef_encode:win_to_utf8(<<16#30>>)), %% DIGIT ZERO 86 | ?_assertEqual(<<16#0031/utf8>>, uef_encode:win_to_utf8(<<16#31>>)), %% DIGIT ONE 87 | ?_assertEqual(<<16#0032/utf8>>, uef_encode:win_to_utf8(<<16#32>>)), %% DIGIT TWO 88 | ?_assertEqual(<<16#0033/utf8>>, uef_encode:win_to_utf8(<<16#33>>)), %% DIGIT THREE 89 | ?_assertEqual(<<16#0034/utf8>>, uef_encode:win_to_utf8(<<16#34>>)), %% DIGIT FOUR 90 | ?_assertEqual(<<16#0035/utf8>>, uef_encode:win_to_utf8(<<16#35>>)), %% DIGIT FIVE 91 | ?_assertEqual(<<16#0036/utf8>>, uef_encode:win_to_utf8(<<16#36>>)), %% DIGIT SIX 92 | ?_assertEqual(<<16#0037/utf8>>, uef_encode:win_to_utf8(<<16#37>>)), %% DIGIT SEVEN 93 | ?_assertEqual(<<16#0038/utf8>>, uef_encode:win_to_utf8(<<16#38>>)), %% DIGIT EIGHT 94 | ?_assertEqual(<<16#0039/utf8>>, uef_encode:win_to_utf8(<<16#39>>)), %% DIGIT NINE 95 | ?_assertEqual(<<16#003A/utf8>>, uef_encode:win_to_utf8(<<16#3A>>)), %% COLON 96 | ?_assertEqual(<<16#003B/utf8>>, uef_encode:win_to_utf8(<<16#3B>>)), %% SEMICOLON 97 | ?_assertEqual(<<16#003C/utf8>>, uef_encode:win_to_utf8(<<16#3C>>)), %% LESS-THAN SIGN 98 | ?_assertEqual(<<16#003D/utf8>>, uef_encode:win_to_utf8(<<16#3D>>)), %% EQUALS SIGN 99 | ?_assertEqual(<<16#003E/utf8>>, uef_encode:win_to_utf8(<<16#3E>>)), %% GREATER-THAN SIGN 100 | ?_assertEqual(<<16#003F/utf8>>, uef_encode:win_to_utf8(<<16#3F>>)), %% QUESTION MARK 101 | ?_assertEqual(<<16#0040/utf8>>, uef_encode:win_to_utf8(<<16#40>>)), %% COMMERCIAL AT 102 | ?_assertEqual(<<16#0041/utf8>>, uef_encode:win_to_utf8(<<16#41>>)), %% LATIN CAPITAL LETTER A 103 | ?_assertEqual(<<16#0042/utf8>>, uef_encode:win_to_utf8(<<16#42>>)), %% LATIN CAPITAL LETTER B 104 | ?_assertEqual(<<16#0043/utf8>>, uef_encode:win_to_utf8(<<16#43>>)), %% LATIN CAPITAL LETTER C 105 | ?_assertEqual(<<16#0044/utf8>>, uef_encode:win_to_utf8(<<16#44>>)), %% LATIN CAPITAL LETTER D 106 | ?_assertEqual(<<16#0045/utf8>>, uef_encode:win_to_utf8(<<16#45>>)), %% LATIN CAPITAL LETTER E 107 | ?_assertEqual(<<16#0046/utf8>>, uef_encode:win_to_utf8(<<16#46>>)), %% LATIN CAPITAL LETTER F 108 | ?_assertEqual(<<16#0047/utf8>>, uef_encode:win_to_utf8(<<16#47>>)), %% LATIN CAPITAL LETTER G 109 | ?_assertEqual(<<16#0048/utf8>>, uef_encode:win_to_utf8(<<16#48>>)), %% LATIN CAPITAL LETTER H 110 | ?_assertEqual(<<16#0049/utf8>>, uef_encode:win_to_utf8(<<16#49>>)), %% LATIN CAPITAL LETTER I 111 | ?_assertEqual(<<16#004A/utf8>>, uef_encode:win_to_utf8(<<16#4A>>)), %% LATIN CAPITAL LETTER J 112 | ?_assertEqual(<<16#004B/utf8>>, uef_encode:win_to_utf8(<<16#4B>>)), %% LATIN CAPITAL LETTER K 113 | ?_assertEqual(<<16#004C/utf8>>, uef_encode:win_to_utf8(<<16#4C>>)), %% LATIN CAPITAL LETTER L 114 | ?_assertEqual(<<16#004D/utf8>>, uef_encode:win_to_utf8(<<16#4D>>)), %% LATIN CAPITAL LETTER M 115 | ?_assertEqual(<<16#004E/utf8>>, uef_encode:win_to_utf8(<<16#4E>>)), %% LATIN CAPITAL LETTER N 116 | ?_assertEqual(<<16#004F/utf8>>, uef_encode:win_to_utf8(<<16#4F>>)), %% LATIN CAPITAL LETTER O 117 | ?_assertEqual(<<16#0050/utf8>>, uef_encode:win_to_utf8(<<16#50>>)), %% LATIN CAPITAL LETTER P 118 | ?_assertEqual(<<16#0051/utf8>>, uef_encode:win_to_utf8(<<16#51>>)), %% LATIN CAPITAL LETTER Q 119 | ?_assertEqual(<<16#0052/utf8>>, uef_encode:win_to_utf8(<<16#52>>)), %% LATIN CAPITAL LETTER R 120 | ?_assertEqual(<<16#0053/utf8>>, uef_encode:win_to_utf8(<<16#53>>)), %% LATIN CAPITAL LETTER S 121 | ?_assertEqual(<<16#0054/utf8>>, uef_encode:win_to_utf8(<<16#54>>)), %% LATIN CAPITAL LETTER T 122 | ?_assertEqual(<<16#0055/utf8>>, uef_encode:win_to_utf8(<<16#55>>)), %% LATIN CAPITAL LETTER U 123 | ?_assertEqual(<<16#0056/utf8>>, uef_encode:win_to_utf8(<<16#56>>)), %% LATIN CAPITAL LETTER V 124 | ?_assertEqual(<<16#0057/utf8>>, uef_encode:win_to_utf8(<<16#57>>)), %% LATIN CAPITAL LETTER W 125 | ?_assertEqual(<<16#0058/utf8>>, uef_encode:win_to_utf8(<<16#58>>)), %% LATIN CAPITAL LETTER X 126 | ?_assertEqual(<<16#0059/utf8>>, uef_encode:win_to_utf8(<<16#59>>)), %% LATIN CAPITAL LETTER Y 127 | ?_assertEqual(<<16#005A/utf8>>, uef_encode:win_to_utf8(<<16#5A>>)), %% LATIN CAPITAL LETTER Z 128 | ?_assertEqual(<<16#005B/utf8>>, uef_encode:win_to_utf8(<<16#5B>>)), %% LEFT SQUARE BRACKET 129 | ?_assertEqual(<<16#005C/utf8>>, uef_encode:win_to_utf8(<<16#5C>>)), %% REVERSE SOLIDUS 130 | ?_assertEqual(<<16#005D/utf8>>, uef_encode:win_to_utf8(<<16#5D>>)), %% RIGHT SQUARE BRACKET 131 | ?_assertEqual(<<16#005E/utf8>>, uef_encode:win_to_utf8(<<16#5E>>)), %% CIRCUMFLEX ACCENT 132 | ?_assertEqual(<<16#005F/utf8>>, uef_encode:win_to_utf8(<<16#5F>>)), %% LOW LINE 133 | ?_assertEqual(<<16#0060/utf8>>, uef_encode:win_to_utf8(<<16#60>>)), %% GRAVE ACCENT 134 | ?_assertEqual(<<16#0061/utf8>>, uef_encode:win_to_utf8(<<16#61>>)), %% LATIN SMALL LETTER A 135 | ?_assertEqual(<<16#0062/utf8>>, uef_encode:win_to_utf8(<<16#62>>)), %% LATIN SMALL LETTER B 136 | ?_assertEqual(<<16#0063/utf8>>, uef_encode:win_to_utf8(<<16#63>>)), %% LATIN SMALL LETTER C 137 | ?_assertEqual(<<16#0064/utf8>>, uef_encode:win_to_utf8(<<16#64>>)), %% LATIN SMALL LETTER D 138 | ?_assertEqual(<<16#0065/utf8>>, uef_encode:win_to_utf8(<<16#65>>)), %% LATIN SMALL LETTER E 139 | ?_assertEqual(<<16#0066/utf8>>, uef_encode:win_to_utf8(<<16#66>>)), %% LATIN SMALL LETTER F 140 | ?_assertEqual(<<16#0067/utf8>>, uef_encode:win_to_utf8(<<16#67>>)), %% LATIN SMALL LETTER G 141 | ?_assertEqual(<<16#0068/utf8>>, uef_encode:win_to_utf8(<<16#68>>)), %% LATIN SMALL LETTER H 142 | ?_assertEqual(<<16#0069/utf8>>, uef_encode:win_to_utf8(<<16#69>>)), %% LATIN SMALL LETTER I 143 | ?_assertEqual(<<16#006A/utf8>>, uef_encode:win_to_utf8(<<16#6A>>)), %% LATIN SMALL LETTER J 144 | ?_assertEqual(<<16#006B/utf8>>, uef_encode:win_to_utf8(<<16#6B>>)), %% LATIN SMALL LETTER K 145 | ?_assertEqual(<<16#006C/utf8>>, uef_encode:win_to_utf8(<<16#6C>>)), %% LATIN SMALL LETTER L 146 | ?_assertEqual(<<16#006D/utf8>>, uef_encode:win_to_utf8(<<16#6D>>)), %% LATIN SMALL LETTER M 147 | ?_assertEqual(<<16#006E/utf8>>, uef_encode:win_to_utf8(<<16#6E>>)), %% LATIN SMALL LETTER N 148 | ?_assertEqual(<<16#006F/utf8>>, uef_encode:win_to_utf8(<<16#6F>>)), %% LATIN SMALL LETTER O 149 | ?_assertEqual(<<16#0070/utf8>>, uef_encode:win_to_utf8(<<16#70>>)), %% LATIN SMALL LETTER P 150 | ?_assertEqual(<<16#0071/utf8>>, uef_encode:win_to_utf8(<<16#71>>)), %% LATIN SMALL LETTER Q 151 | ?_assertEqual(<<16#0072/utf8>>, uef_encode:win_to_utf8(<<16#72>>)), %% LATIN SMALL LETTER R 152 | ?_assertEqual(<<16#0073/utf8>>, uef_encode:win_to_utf8(<<16#73>>)), %% LATIN SMALL LETTER S 153 | ?_assertEqual(<<16#0074/utf8>>, uef_encode:win_to_utf8(<<16#74>>)), %% LATIN SMALL LETTER T 154 | ?_assertEqual(<<16#0075/utf8>>, uef_encode:win_to_utf8(<<16#75>>)), %% LATIN SMALL LETTER U 155 | ?_assertEqual(<<16#0076/utf8>>, uef_encode:win_to_utf8(<<16#76>>)), %% LATIN SMALL LETTER V 156 | ?_assertEqual(<<16#0077/utf8>>, uef_encode:win_to_utf8(<<16#77>>)), %% LATIN SMALL LETTER W 157 | ?_assertEqual(<<16#0078/utf8>>, uef_encode:win_to_utf8(<<16#78>>)), %% LATIN SMALL LETTER X 158 | ?_assertEqual(<<16#0079/utf8>>, uef_encode:win_to_utf8(<<16#79>>)), %% LATIN SMALL LETTER Y 159 | ?_assertEqual(<<16#007A/utf8>>, uef_encode:win_to_utf8(<<16#7A>>)), %% LATIN SMALL LETTER Z 160 | ?_assertEqual(<<16#007B/utf8>>, uef_encode:win_to_utf8(<<16#7B>>)), %% LEFT CURLY BRACKET 161 | ?_assertEqual(<<16#007C/utf8>>, uef_encode:win_to_utf8(<<16#7C>>)), %% VERTICAL LINE 162 | ?_assertEqual(<<16#007D/utf8>>, uef_encode:win_to_utf8(<<16#7D>>)), %% RIGHT CURLY BRACKET 163 | ?_assertEqual(<<16#007E/utf8>>, uef_encode:win_to_utf8(<<16#7E>>)), %% TILDE 164 | ?_assertEqual(<<16#007F/utf8>>, uef_encode:win_to_utf8(<<16#7F>>)), %% DELETE 165 | ?_assertEqual(<<16#0402/utf8>>, uef_encode:win_to_utf8(<<16#80>>)), %% CYRILLIC CAPITAL LETTER DJE 166 | ?_assertEqual(<<16#0403/utf8>>, uef_encode:win_to_utf8(<<16#81>>)), %% CYRILLIC CAPITAL LETTER GJE 167 | ?_assertEqual(<<16#201A/utf8>>, uef_encode:win_to_utf8(<<16#82>>)), %% SINGLE LOW-9 QUOTATION MARK 168 | ?_assertEqual(<<16#0453/utf8>>, uef_encode:win_to_utf8(<<16#83>>)), %% CYRILLIC SMALL LETTER GJE 169 | ?_assertEqual(<<16#201E/utf8>>, uef_encode:win_to_utf8(<<16#84>>)), %% DOUBLE LOW-9 QUOTATION MARK 170 | ?_assertEqual(<<16#2026/utf8>>, uef_encode:win_to_utf8(<<16#85>>)), %% HORIZONTAL ELLIPSIS 171 | ?_assertEqual(<<16#2020/utf8>>, uef_encode:win_to_utf8(<<16#86>>)), %% DAGGER 172 | ?_assertEqual(<<16#2021/utf8>>, uef_encode:win_to_utf8(<<16#87>>)), %% DOUBLE DAGGER 173 | ?_assertEqual(<<16#20AC/utf8>>, uef_encode:win_to_utf8(<<16#88>>)), %% EURO SIGN 174 | ?_assertEqual(<<16#2030/utf8>>, uef_encode:win_to_utf8(<<16#89>>)), %% PER MILLE SIGN 175 | ?_assertEqual(<<16#0409/utf8>>, uef_encode:win_to_utf8(<<16#8A>>)), %% CYRILLIC CAPITAL LETTER LJE 176 | ?_assertEqual(<<16#2039/utf8>>, uef_encode:win_to_utf8(<<16#8B>>)), %% SINGLE LEFT-POINTING ANGLE QUOTATION MARK 177 | ?_assertEqual(<<16#040A/utf8>>, uef_encode:win_to_utf8(<<16#8C>>)), %% CYRILLIC CAPITAL LETTER NJE 178 | ?_assertEqual(<<16#040C/utf8>>, uef_encode:win_to_utf8(<<16#8D>>)), %% CYRILLIC CAPITAL LETTER KJE 179 | ?_assertEqual(<<16#040B/utf8>>, uef_encode:win_to_utf8(<<16#8E>>)), %% CYRILLIC CAPITAL LETTER TSHE 180 | ?_assertEqual(<<16#040F/utf8>>, uef_encode:win_to_utf8(<<16#8F>>)), %% CYRILLIC CAPITAL LETTER DZHE 181 | ?_assertEqual(<<16#0452/utf8>>, uef_encode:win_to_utf8(<<16#90>>)), %% CYRILLIC SMALL LETTER DJE 182 | ?_assertEqual(<<16#2018/utf8>>, uef_encode:win_to_utf8(<<16#91>>)), %% LEFT SINGLE QUOTATION MARK 183 | ?_assertEqual(<<16#2019/utf8>>, uef_encode:win_to_utf8(<<16#92>>)), %% RIGHT SINGLE QUOTATION MARK 184 | ?_assertEqual(<<16#201C/utf8>>, uef_encode:win_to_utf8(<<16#93>>)), %% LEFT DOUBLE QUOTATION MARK 185 | ?_assertEqual(<<16#201D/utf8>>, uef_encode:win_to_utf8(<<16#94>>)), %% RIGHT DOUBLE QUOTATION MARK 186 | ?_assertEqual(<<16#2024/utf8>>, uef_encode:win_to_utf8(<<16#95>>)), %% BULLET 187 | ?_assertEqual(<<16#2013/utf8>>, uef_encode:win_to_utf8(<<16#96>>)), %% EN DASH 188 | ?_assertEqual(<<16#2014/utf8>>, uef_encode:win_to_utf8(<<16#97>>)), %% EM DASH 189 | ?_assertEqual(<<16#2122/utf8>>, uef_encode:win_to_utf8(<<16#99>>)), %% TRADE MARK SIGN 190 | ?_assertEqual(<<16#0459/utf8>>, uef_encode:win_to_utf8(<<16#9A>>)), %% CYRILLIC SMALL LETTER LJE 191 | ?_assertEqual(<<16#203A/utf8>>, uef_encode:win_to_utf8(<<16#9B>>)), %% SINGLE RIGHT-POINTING ANGLE QUOTATION MARK 192 | ?_assertEqual(<<16#045A/utf8>>, uef_encode:win_to_utf8(<<16#9C>>)), %% CYRILLIC SMALL LETTER NJE 193 | ?_assertEqual(<<16#045C/utf8>>, uef_encode:win_to_utf8(<<16#9D>>)), %% CYRILLIC SMALL LETTER KJE 194 | ?_assertEqual(<<16#045B/utf8>>, uef_encode:win_to_utf8(<<16#9E>>)), %% CYRILLIC SMALL LETTER TSHE 195 | ?_assertEqual(<<16#045F/utf8>>, uef_encode:win_to_utf8(<<16#9F>>)), %% CYRILLIC SMALL LETTER DZHE 196 | ?_assertEqual(<<16#00A0/utf8>>, uef_encode:win_to_utf8(<<16#A0>>)), %% NO-BREAK SPACE 197 | ?_assertEqual(<<16#040E/utf8>>, uef_encode:win_to_utf8(<<16#A1>>)), %% CYRILLIC CAPITAL LETTER SHORT U 198 | ?_assertEqual(<<16#045E/utf8>>, uef_encode:win_to_utf8(<<16#A2>>)), %% CYRILLIC SMALL LETTER SHORT U 199 | ?_assertEqual(<<16#0408/utf8>>, uef_encode:win_to_utf8(<<16#A3>>)), %% CYRILLIC CAPITAL LETTER JE 200 | ?_assertEqual(<<16#00A4/utf8>>, uef_encode:win_to_utf8(<<16#A4>>)), %% CURRENCY SIGN 201 | ?_assertEqual(<<16#0490/utf8>>, uef_encode:win_to_utf8(<<16#A5>>)), %% CYRILLIC CAPITAL LETTER GHE WITH UPTURN 202 | ?_assertEqual(<<16#00A6/utf8>>, uef_encode:win_to_utf8(<<16#A6>>)), %% BROKEN BAR 203 | ?_assertEqual(<<16#00A7/utf8>>, uef_encode:win_to_utf8(<<16#A7>>)), %% SECTION SIGN 204 | ?_assertEqual(<<16#0401/utf8>>, uef_encode:win_to_utf8(<<16#A8>>)), %% CYRILLIC CAPITAL LETTER IO 205 | ?_assertEqual(<<16#00A9/utf8>>, uef_encode:win_to_utf8(<<16#A9>>)), %% COPYRIGHT SIGN 206 | ?_assertEqual(<<16#0404/utf8>>, uef_encode:win_to_utf8(<<16#AA>>)), %% CYRILLIC CAPITAL LETTER UKRAINIAN IE 207 | ?_assertEqual(<<16#00AB/utf8>>, uef_encode:win_to_utf8(<<16#AB>>)), %% LEFT-POINTING DOUBLE ANGLE QUOTATION MARK 208 | ?_assertEqual(<<16#00AC/utf8>>, uef_encode:win_to_utf8(<<16#AC>>)), %% NOT SIGN 209 | ?_assertEqual(<<16#00AD/utf8>>, uef_encode:win_to_utf8(<<16#AD>>)), %% SOFT HYPHEN 210 | ?_assertEqual(<<16#00AE/utf8>>, uef_encode:win_to_utf8(<<16#AE>>)), %% REGISTERED SIGN 211 | ?_assertEqual(<<16#0407/utf8>>, uef_encode:win_to_utf8(<<16#AF>>)), %% CYRILLIC CAPITAL LETTER YI 212 | ?_assertEqual(<<16#00B0/utf8>>, uef_encode:win_to_utf8(<<16#B0>>)), %% DEGREE SIGN 213 | ?_assertEqual(<<16#00B1/utf8>>, uef_encode:win_to_utf8(<<16#B1>>)), %% PLUS-MINUS SIGN 214 | ?_assertEqual(<<16#0406/utf8>>, uef_encode:win_to_utf8(<<16#B2>>)), %% CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I 215 | ?_assertEqual(<<16#0456/utf8>>, uef_encode:win_to_utf8(<<16#B3>>)), %% CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I 216 | ?_assertEqual(<<16#0491/utf8>>, uef_encode:win_to_utf8(<<16#B4>>)), %% CYRILLIC SMALL LETTER GHE WITH UPTURN 217 | ?_assertEqual(<<16#00B5/utf8>>, uef_encode:win_to_utf8(<<16#B5>>)), %% MICRO SIGN 218 | ?_assertEqual(<<16#00B6/utf8>>, uef_encode:win_to_utf8(<<16#B6>>)), %% PILCROW SIGN 219 | ?_assertEqual(<<16#00B7/utf8>>, uef_encode:win_to_utf8(<<16#B7>>)), %% MIDDLE DOT 220 | ?_assertEqual(<<16#0451/utf8>>, uef_encode:win_to_utf8(<<16#B8>>)), %% CYRILLIC SMALL LETTER IO 221 | ?_assertEqual(<<16#2116/utf8>>, uef_encode:win_to_utf8(<<16#B9>>)), %% NUMERO SIGN 222 | ?_assertEqual(<<16#0454/utf8>>, uef_encode:win_to_utf8(<<16#BA>>)), %% CYRILLIC SMALL LETTER UKRAINIAN IE 223 | ?_assertEqual(<<16#00BB/utf8>>, uef_encode:win_to_utf8(<<16#BB>>)), %% RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK 224 | ?_assertEqual(<<16#0458/utf8>>, uef_encode:win_to_utf8(<<16#BC>>)), %% CYRILLIC SMALL LETTER JE 225 | ?_assertEqual(<<16#0405/utf8>>, uef_encode:win_to_utf8(<<16#BD>>)), %% CYRILLIC CAPITAL LETTER DZE 226 | ?_assertEqual(<<16#0455/utf8>>, uef_encode:win_to_utf8(<<16#BE>>)), %% CYRILLIC SMALL LETTER DZE 227 | ?_assertEqual(<<16#0457/utf8>>, uef_encode:win_to_utf8(<<16#BF>>)), %% CYRILLIC SMALL LETTER YI 228 | ?_assertEqual(<<16#0410/utf8>>, uef_encode:win_to_utf8(<<16#C0>>)), %% CYRILLIC CAPITAL LETTER A 229 | ?_assertEqual(<<16#0411/utf8>>, uef_encode:win_to_utf8(<<16#C1>>)), %% CYRILLIC CAPITAL LETTER BE 230 | ?_assertEqual(<<16#0412/utf8>>, uef_encode:win_to_utf8(<<16#C2>>)), %% CYRILLIC CAPITAL LETTER VE 231 | ?_assertEqual(<<16#0413/utf8>>, uef_encode:win_to_utf8(<<16#C3>>)), %% CYRILLIC CAPITAL LETTER GHE 232 | ?_assertEqual(<<16#0414/utf8>>, uef_encode:win_to_utf8(<<16#C4>>)), %% CYRILLIC CAPITAL LETTER DE 233 | ?_assertEqual(<<16#0415/utf8>>, uef_encode:win_to_utf8(<<16#C5>>)), %% CYRILLIC CAPITAL LETTER IE 234 | ?_assertEqual(<<16#0416/utf8>>, uef_encode:win_to_utf8(<<16#C6>>)), %% CYRILLIC CAPITAL LETTER ZHE 235 | ?_assertEqual(<<16#0417/utf8>>, uef_encode:win_to_utf8(<<16#C7>>)), %% CYRILLIC CAPITAL LETTER ZE 236 | ?_assertEqual(<<16#0418/utf8>>, uef_encode:win_to_utf8(<<16#C8>>)), %% CYRILLIC CAPITAL LETTER I 237 | ?_assertEqual(<<16#0419/utf8>>, uef_encode:win_to_utf8(<<16#C9>>)), %% CYRILLIC CAPITAL LETTER SHORT I 238 | ?_assertEqual(<<16#041A/utf8>>, uef_encode:win_to_utf8(<<16#CA>>)), %% CYRILLIC CAPITAL LETTER KA 239 | ?_assertEqual(<<16#041B/utf8>>, uef_encode:win_to_utf8(<<16#CB>>)), %% CYRILLIC CAPITAL LETTER EL 240 | ?_assertEqual(<<16#041C/utf8>>, uef_encode:win_to_utf8(<<16#CC>>)), %% CYRILLIC CAPITAL LETTER EM 241 | ?_assertEqual(<<16#041D/utf8>>, uef_encode:win_to_utf8(<<16#CD>>)), %% CYRILLIC CAPITAL LETTER EN 242 | ?_assertEqual(<<16#041E/utf8>>, uef_encode:win_to_utf8(<<16#CE>>)), %% CYRILLIC CAPITAL LETTER O 243 | ?_assertEqual(<<16#041F/utf8>>, uef_encode:win_to_utf8(<<16#CF>>)), %% CYRILLIC CAPITAL LETTER PE 244 | ?_assertEqual(<<16#0420/utf8>>, uef_encode:win_to_utf8(<<16#D0>>)), %% CYRILLIC CAPITAL LETTER ER 245 | ?_assertEqual(<<16#0421/utf8>>, uef_encode:win_to_utf8(<<16#D1>>)), %% CYRILLIC CAPITAL LETTER ES 246 | ?_assertEqual(<<16#0422/utf8>>, uef_encode:win_to_utf8(<<16#D2>>)), %% CYRILLIC CAPITAL LETTER TE 247 | ?_assertEqual(<<16#0423/utf8>>, uef_encode:win_to_utf8(<<16#D3>>)), %% CYRILLIC CAPITAL LETTER U 248 | ?_assertEqual(<<16#0424/utf8>>, uef_encode:win_to_utf8(<<16#D4>>)), %% CYRILLIC CAPITAL LETTER EF 249 | ?_assertEqual(<<16#0425/utf8>>, uef_encode:win_to_utf8(<<16#D5>>)), %% CYRILLIC CAPITAL LETTER HA 250 | ?_assertEqual(<<16#0426/utf8>>, uef_encode:win_to_utf8(<<16#D6>>)), %% CYRILLIC CAPITAL LETTER TSE 251 | ?_assertEqual(<<16#0427/utf8>>, uef_encode:win_to_utf8(<<16#D7>>)), %% CYRILLIC CAPITAL LETTER CHE 252 | ?_assertEqual(<<16#0428/utf8>>, uef_encode:win_to_utf8(<<16#D8>>)), %% CYRILLIC CAPITAL LETTER SHA 253 | ?_assertEqual(<<16#0429/utf8>>, uef_encode:win_to_utf8(<<16#D9>>)), %% CYRILLIC CAPITAL LETTER SHCHA 254 | ?_assertEqual(<<16#042A/utf8>>, uef_encode:win_to_utf8(<<16#DA>>)), %% CYRILLIC CAPITAL LETTER HARD SIGN 255 | ?_assertEqual(<<16#042B/utf8>>, uef_encode:win_to_utf8(<<16#DB>>)), %% CYRILLIC CAPITAL LETTER YERU 256 | ?_assertEqual(<<16#042C/utf8>>, uef_encode:win_to_utf8(<<16#DC>>)), %% CYRILLIC CAPITAL LETTER SOFT SIGN 257 | ?_assertEqual(<<16#042D/utf8>>, uef_encode:win_to_utf8(<<16#DD>>)), %% CYRILLIC CAPITAL LETTER E 258 | ?_assertEqual(<<16#042E/utf8>>, uef_encode:win_to_utf8(<<16#DE>>)), %% CYRILLIC CAPITAL LETTER YU 259 | ?_assertEqual(<<16#042F/utf8>>, uef_encode:win_to_utf8(<<16#DF>>)), %% CYRILLIC CAPITAL LETTER YA 260 | ?_assertEqual(<<16#0430/utf8>>, uef_encode:win_to_utf8(<<16#E0>>)), %% CYRILLIC SMALL LETTER A 261 | ?_assertEqual(<<16#0431/utf8>>, uef_encode:win_to_utf8(<<16#E1>>)), %% CYRILLIC SMALL LETTER BE 262 | ?_assertEqual(<<16#0432/utf8>>, uef_encode:win_to_utf8(<<16#E2>>)), %% CYRILLIC SMALL LETTER VE 263 | ?_assertEqual(<<16#0433/utf8>>, uef_encode:win_to_utf8(<<16#E3>>)), %% CYRILLIC SMALL LETTER GHE 264 | ?_assertEqual(<<16#0434/utf8>>, uef_encode:win_to_utf8(<<16#E4>>)), %% CYRILLIC SMALL LETTER DE 265 | ?_assertEqual(<<16#0435/utf8>>, uef_encode:win_to_utf8(<<16#E5>>)), %% CYRILLIC SMALL LETTER IE 266 | ?_assertEqual(<<16#0436/utf8>>, uef_encode:win_to_utf8(<<16#E6>>)), %% CYRILLIC SMALL LETTER ZHE 267 | ?_assertEqual(<<16#0437/utf8>>, uef_encode:win_to_utf8(<<16#E7>>)), %% CYRILLIC SMALL LETTER ZE 268 | ?_assertEqual(<<16#0438/utf8>>, uef_encode:win_to_utf8(<<16#E8>>)), %% CYRILLIC SMALL LETTER I 269 | ?_assertEqual(<<16#0439/utf8>>, uef_encode:win_to_utf8(<<16#E9>>)), %% CYRILLIC SMALL LETTER SHORT I 270 | ?_assertEqual(<<16#043A/utf8>>, uef_encode:win_to_utf8(<<16#EA>>)), %% CYRILLIC SMALL LETTER KA 271 | ?_assertEqual(<<16#043B/utf8>>, uef_encode:win_to_utf8(<<16#EB>>)), %% CYRILLIC SMALL LETTER EL 272 | ?_assertEqual(<<16#043C/utf8>>, uef_encode:win_to_utf8(<<16#EC>>)), %% CYRILLIC SMALL LETTER EM 273 | ?_assertEqual(<<16#043D/utf8>>, uef_encode:win_to_utf8(<<16#ED>>)), %% CYRILLIC SMALL LETTER EN 274 | ?_assertEqual(<<16#043E/utf8>>, uef_encode:win_to_utf8(<<16#EE>>)), %% CYRILLIC SMALL LETTER O 275 | ?_assertEqual(<<16#043F/utf8>>, uef_encode:win_to_utf8(<<16#EF>>)), %% CYRILLIC SMALL LETTER PE 276 | ?_assertEqual(<<16#0440/utf8>>, uef_encode:win_to_utf8(<<16#F0>>)), %% CYRILLIC SMALL LETTER ER 277 | ?_assertEqual(<<16#0441/utf8>>, uef_encode:win_to_utf8(<<16#F1>>)), %% CYRILLIC SMALL LETTER ES 278 | ?_assertEqual(<<16#0442/utf8>>, uef_encode:win_to_utf8(<<16#F2>>)), %% CYRILLIC SMALL LETTER TE 279 | ?_assertEqual(<<16#0443/utf8>>, uef_encode:win_to_utf8(<<16#F3>>)), %% CYRILLIC SMALL LETTER U 280 | ?_assertEqual(<<16#0444/utf8>>, uef_encode:win_to_utf8(<<16#F4>>)), %% CYRILLIC SMALL LETTER EF 281 | ?_assertEqual(<<16#0445/utf8>>, uef_encode:win_to_utf8(<<16#F5>>)), %% CYRILLIC SMALL LETTER HA 282 | ?_assertEqual(<<16#0446/utf8>>, uef_encode:win_to_utf8(<<16#F6>>)), %% CYRILLIC SMALL LETTER TSE 283 | ?_assertEqual(<<16#0447/utf8>>, uef_encode:win_to_utf8(<<16#F7>>)), %% CYRILLIC SMALL LETTER CHE 284 | ?_assertEqual(<<16#0448/utf8>>, uef_encode:win_to_utf8(<<16#F8>>)), %% CYRILLIC SMALL LETTER SHA 285 | ?_assertEqual(<<16#0449/utf8>>, uef_encode:win_to_utf8(<<16#F9>>)), %% CYRILLIC SMALL LETTER SHCHA 286 | ?_assertEqual(<<16#044A/utf8>>, uef_encode:win_to_utf8(<<16#FA>>)), %% CYRILLIC SMALL LETTER HARD SIGN 287 | ?_assertEqual(<<16#044B/utf8>>, uef_encode:win_to_utf8(<<16#FB>>)), %% CYRILLIC SMALL LETTER YERU 288 | ?_assertEqual(<<16#044C/utf8>>, uef_encode:win_to_utf8(<<16#FC>>)), %% CYRILLIC SMALL LETTER SOFT SIGN 289 | ?_assertEqual(<<16#044D/utf8>>, uef_encode:win_to_utf8(<<16#FD>>)), %% CYRILLIC SMALL LETTER E 290 | ?_assertEqual(<<16#044E/utf8>>, uef_encode:win_to_utf8(<<16#FE>>)), %% CYRILLIC SMALL LETTER YU 291 | ?_assertEqual(<<16#044F/utf8>>, uef_encode:win_to_utf8(<<16#FF>>)) %% CYRILLIC SMALL LETTER YA 292 | ]. 293 | -------------------------------------------------------------------------------- /test/uef_file_tests.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2019-2024, Sergei Semichev . All Rights Reserved. 2 | %% 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | 15 | -module(uef_file_tests). 16 | 17 | -include_lib("kernel/include/file.hrl"). 18 | -include_lib("eunit/include/eunit.hrl"). 19 | 20 | %%%------------------------------------------------------------------------------ 21 | %%% Tests 22 | %%%------------------------------------------------------------------------------ 23 | 24 | read_file_info_fast_test_() -> 25 | TestDataDir = get_test_data_dir(), 26 | ExistingFile = filename:join([TestDataDir, "uef_file_1.txt"]), 27 | NonExistingFile = filename:join([TestDataDir, "uef_file_nonexisting.txt"]), 28 | [ 29 | ?_assertMatch({ok, #file_info{type = regular}}, uef_file:read_file_info_fast(ExistingFile)), 30 | ?_assertEqual({error, enoent}, uef_file:read_file_info_fast(NonExistingFile)) 31 | ]. 32 | 33 | read_file_fast_test_() -> 34 | TestDataDir = get_test_data_dir(), 35 | ExistingFile = filename:join([TestDataDir, "uef_file_1.txt"]), 36 | NonExistingFile = filename:join([TestDataDir, "uef_file_nonexisting.txt"]), 37 | ExpectedData = case os:type() of 38 | {win32, _} -> <<"test1\r\n">>; 39 | _ -> <<"test1\n">> 40 | end, 41 | [ 42 | ?_assertEqual({ok, ExpectedData}, uef_file:read_file_fast(ExistingFile)), 43 | ?_assertEqual({error, enoent}, uef_file:read_file_fast(NonExistingFile)) 44 | ]. 45 | 46 | 47 | %%%------------------------------------------------------------------------------ 48 | %%% Internal functions 49 | %%%------------------------------------------------------------------------------ 50 | 51 | get_test_data_dir() -> 52 | LibDir = code:lib_dir(uef), 53 | filename:join([LibDir, "test", "data"]). 54 | -------------------------------------------------------------------------------- /test/uef_format_tests.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2019-2024, Sergei Semichev . All Rights Reserved. 2 | %% 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | 15 | -module(uef_format_tests). 16 | 17 | -include_lib("eunit/include/eunit.hrl"). 18 | 19 | %%%------------------------------------------------------------------------------ 20 | %%% Tests 21 | %%%------------------------------------------------------------------------------ 22 | 23 | format_number_test_() -> 24 | [ 25 | ?_assertEqual(<<"1.00">>, uef_format:format_number(1, 2, 2, #{})), 26 | ?_assertEqual(<<"1.99">>, uef_format:format_number(1.99, 2, 2, #{})), 27 | ?_assertEqual(<<"2.00">>, uef_format:format_number(1.99, 1, 2, #{})), 28 | ?_assertEqual(<<"10">>, uef_format:format_number(10, 2, 0, #{})), 29 | ?_assertEqual(<<"1 000.00">>, uef_format:format_number(1000, 0, 2, #{thousands_sep => " "})), 30 | ?_assertEqual(<<"1 000.00">>, uef_format:format_number(1000, 0, 2, #{thousands_sep => [32]})), 31 | ?_assertEqual(<<"1 000 999.00">>, uef_format:format_number(1000999, 2, 2, #{thousands_sep => <<" ">>})), 32 | ?_assertEqual(<<"2,000,000.00">>, uef_format:format_number(2000000, 2, 2, #{thousands_sep => <<",">>})), 33 | ?_assertEqual(<<"9 999 999 999.00">>, uef_format:format_number(9999999999, 2, 2, #{thousands_sep => <<" ">>})), 34 | ?_assertEqual(<<"99 999 999 999.99">>, uef_format:format_price(99999999999.99, 2, #{thousands_sep => <<" ">>})), 35 | ?_assertEqual(<<"999 999 999 999.99">>, uef_format:format_price(999999999999.99, 2, #{thousands_sep => <<" ">>})), 36 | ?_assertEqual(<<"999,999,999,999.99">>, uef_format:format_price(999999999999.99, 2, #{thousands_sep => <<",">>})), 37 | ?_assertEqual(<<"USD 1,234,567,890==4600">>, uef_format:format_number(1234567890.4567, 2, 4, #{thousands_sep => ",", decimal_point => "==", cur_symbol => "USD", cur_sep => " ", cur_pos => left})), 38 | ?_assertEqual(<<"$1000.88">>, uef_format:format_price(1000.8767, 4, "$")), 39 | ?_assertEqual(<<"1000.88 руб."/utf8>>, uef_format:format_price(1000.8767, 4, #{cur_symbol => <<"руб."/utf8>>, cur_sep => " ", cur_pos => right})), 40 | ?_assertEqual(<<"1000.88 руб."/utf8>>, uef_format:format_price(1000.8767, 4, #{cur_symbol => "руб.", cur_sep => " ", cur_pos => right})), 41 | ?_assertEqual(<<"€€1000.00"/utf8>>, uef_format:format_price(1000, 4, #{cur_symbol => "€", cur_sep => "€", cur_pos => left})), 42 | ?_assertEqual(uef_format:format_number(100, 2, 3), uef_format:format_number(100, 2, 3, #{})), 43 | ?_assertEqual(<<"199.500">>, uef_format:format_number(199.4567, 1, 3)), 44 | ?_assertEqual(<<"-199.500">>, uef_format:format_number(-199.4567, 1, 3)), 45 | ?_assertEqual(<<$1, 1, $0,$0,$0, $., $0,$0>>, uef_format:format_number(1000, 0, 2, #{thousands_sep => [1]})), 46 | ?_assertEqual(<<"1YB">>, uef_format:format_bytes(1 bsl 80, #{base => 2})), 47 | ?_assertEqual(uef_format:format_price(1000), uef_format:format_price(1000, 2)), 48 | ?_assertEqual(uef_format:format_price(1000), uef_format:format_price(1000, 2, <<>>)), 49 | ?_assertEqual(uef_format:format_price(1000), uef_format:format_number(1000, 2, 2, #{})), 50 | ?_assertError(badarg, uef_format:format_number(1000, 0, 2, #{thousands_sep => [-1]})), 51 | ?_assertError(badarg, uef_format:format_number(1000, 0, 2, #{thousands_sep => 32})), 52 | ?_assertError({invalid_units, 'BAD_UNITS'}, uef_format:format_bytes(100, #{units => 'BAD_UNITS'})), 53 | ?_assertError({invalid_output_type, list}, uef_format:format_bytes(100, #{to_type => list})), 54 | ?_assertError({invalid_separator, " "}, uef_format:format_bytes(100, #{sep => " "})) 55 | ]. 56 | 57 | 58 | format_bytes_test_() -> 59 | KB10_1000 = 10 * 1000, 60 | KB10_1024 = 10 * 1024, 61 | MB10_1000 = 10 * 1000 * 1000, 62 | MB10_1024 = 10 * 1024 * 1024, 63 | [ 64 | ?_assertEqual(<<"0KB">>, uef_format:format_bytes(0)), 65 | ?_assertEqual(<<"0KB">>, uef_format:format_bytes(-0)), 66 | ?_assertEqual(<<"0KB">>, uef_format:format_bytes(1023)), 67 | ?_assertEqual(<<"1KB">>, uef_format:format_bytes(1024)), 68 | ?_assertEqual(<<"0KB">>, uef_format:format_bytes(999, #{base => 10})), 69 | ?_assertEqual(<<"1KB">>, uef_format:format_bytes(1023, #{base => 10})), 70 | ?_assertEqual(<<"-1KB">>, uef_format:format_bytes(-1023, #{base => 10})), 71 | ?_assertEqual(<<"9KB">>, uef_format:format_bytes(KB10_1000)), 72 | ?_assertEqual(<<"10KB">>, uef_format:format_bytes(KB10_1024)), 73 | ?_assertEqual(<<"10 KB">>, uef_format:format_bytes(KB10_1024, #{sep => <<" ">>})), 74 | ?_assertEqual(<<"10|KB">>, uef_format:format_bytes(KB10_1024, #{sep => <<"|">>})), 75 | ?_assertEqual(uef_format:format_bytes(KB10_1000), uef_format:format_bytes(KB10_1000, #{})), 76 | ?_assertEqual(uef_format:format_bytes(MB10_1000), uef_format:format_bytes(MB10_1000, #{})), 77 | ?_assertEqual(uef_format:format_bytes(KB10_1024), uef_format:format_bytes(KB10_1024, #{})), 78 | ?_assertEqual(uef_format:format_bytes(MB10_1024), uef_format:format_bytes(MB10_1024, #{})), 79 | ?_assertEqual(uef_format:format_bytes(10000), uef_format:format_bytes(10000, #{base => 2, units => auto})), 80 | ?_assertEqual(uef_format:format_bytes(10000), uef_format:format_bytes(10000, #{base => 2, units => auto, to_type => bin})), 81 | ?_assertEqual(uef_format:format_bytes(10000), uef_format:format_bytes(10000, #{base => 2, units => auto, to_type => bin, sep => <<>>})), 82 | ?_assertEqual({9, 'KB'}, uef_format:format_bytes(10000, #{to_type => int})), 83 | ?_assertEqual(9, uef_format:format_bytes(KB10_1000, #{to_type => int, units => 'KB'})), 84 | ?_assertEqual(9, uef_format:format_bytes(MB10_1000, #{to_type => int, units => 'MB'})), 85 | ?_assertEqual({9, 'MB'}, uef_format:format_bytes(MB10_1000, #{to_type => int, units => auto})), 86 | ?_assertEqual(10, uef_format:format_bytes(MB10_1024, #{to_type => int, units => 'MB'})), 87 | ?_assertEqual({10, 'MB'}, uef_format:format_bytes(MB10_1024, #{to_type => int})), 88 | ?_assertEqual(<<"0MB">>, uef_format:format_bytes(1000, #{units => 'MB'})), 89 | ?_assertError({badarg, bad_int}, uef_format:format_bytes(bad_int)), 90 | ?_assertError({badarg, bad_opts}, uef_format:format_bytes(1, bad_opts)), 91 | ?_assertError({invalid_base, 16}, uef_format:format_bytes(10000, #{base => 16})) 92 | ]. 93 | 94 | 95 | format_price_test_() -> 96 | [ 97 | ?_assertError({badarg, bad_opts}, uef_format:format_price(1, 2, bad_opts)) 98 | ]. 99 | -------------------------------------------------------------------------------- /test/uef_lists_tests.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2019-2024, Sergei Semichev . All Rights Reserved. 2 | %% 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | 15 | -module(uef_lists_tests). 16 | 17 | -include_lib("eunit/include/eunit.hrl"). 18 | 19 | %%%------------------------------------------------------------------------------ 20 | %%% Tests 21 | %%%------------------------------------------------------------------------------ 22 | 23 | split_list_into_chunks_test_() -> 24 | [ 25 | ?_assertEqual([[1],[2],[3],[4],[5],[6],[7],[8]], uef_lists:split_list_into_chunks([1,2,3,4,5,6,7,8], 1)), 26 | ?_assertEqual([[1,2],[3,4],[5,6],[7,8]], uef_lists:split_list_into_chunks([1,2,3,4,5,6,7,8], 2)), 27 | ?_assertEqual([[1,2,3],[4,5,6],[7,8]], uef_lists:split_list_into_chunks([1,2,3,4,5,6,7,8], 3)), 28 | ?_assertEqual([[1,2,3,4],[5,6,7,8]], uef_lists:split_list_into_chunks([1,2,3,4,5,6,7,8], 4)), 29 | ?_assertEqual([[1,2,3,4,5,6,7,8]], uef_lists:split_list_into_chunks([1,2,3,4,5,6,7,8], 8)), 30 | ?_assertEqual([[1,2,3,4,5,6,7,8]], uef_lists:split_list_into_chunks([1,2,3,4,5,6,7,8], 9)), 31 | ?_assertEqual([[1,2,3,4,5,6,7,8]], uef_lists:split_list_into_chunks([1,2,3,4,5,6,7,8], 99)) 32 | ]. 33 | 34 | lists_to_list_of_tuples_2_test_() -> 35 | [ 36 | ?_assertEqual([{a,1},{a,2},{b,1},{b,2},{c,1},{c,2}], uef_lists:lists_to_list_of_tuples([a,b,c], [1,2])), 37 | ?_assertEqual([{a,1},{a,2},{a,3},{b,1},{b,2},{b,3},{c,1},{c,2},{c,3}], uef_lists:lists_to_list_of_tuples([a,b,c], [1,2,3])) 38 | ]. 39 | 40 | lists_to_list_of_tuples_3_test_() -> 41 | Tuples1 = [ 42 | {a1,a2,a3}, 43 | {a1,a2,b3}, 44 | {a1,b2,a3}, 45 | {a1,b2,b3}, 46 | {a1,c2,a3}, 47 | {a1,c2,b3}, 48 | {b1,a2,a3}, 49 | {b1,a2,b3}, 50 | {b1,b2,a3}, 51 | {b1,b2,b3}, 52 | {b1,c2,a3}, 53 | {b1,c2,b3} 54 | ], 55 | [ 56 | ?_assertEqual(Tuples1, uef_lists:lists_to_list_of_tuples([a1,b1], [a2,b2,c2], [a3,b3])) 57 | ]. 58 | 59 | 60 | lists_search_test_() -> 61 | List = [0,1,2,3,4,5,6,7,8,9,10,11], 62 | TrueFun = fun(X) -> X =:= 9 end, 63 | FalseFun = fun(X) -> X =:= 100 end, 64 | [ 65 | ?_assertEqual({value, 9}, uef_lists:search(TrueFun, List)), 66 | ?_assertEqual(false, uef_lists:search(FalseFun, List)) 67 | ]. 68 | -------------------------------------------------------------------------------- /test/uef_maps_tests.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2019-2024, Sergei Semichev . All Rights Reserved. 2 | %% 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | 15 | -module(uef_maps_tests). 16 | 17 | -include_lib("eunit/include/eunit.hrl"). 18 | 19 | %%%------------------------------------------------------------------------------ 20 | %%% Tests 21 | %%%------------------------------------------------------------------------------ 22 | 23 | find_nested__and__get_nested_test_() -> 24 | Value = value, 25 | M3 = #{3 => Value}, 26 | M2 = #{2 => M3}, 27 | M1 = #{1 => M2}, 28 | M0 = #{0 => M1}, 29 | BadMap = bad_map, 30 | BadList = bad_list, 31 | Default = default, 32 | [ 33 | % find_nested/2 test with {ok, _} 34 | ?_assertEqual({ok, M1}, uef_maps:find_nested([0], M0)), 35 | ?_assertEqual({ok, M2}, uef_maps:find_nested([0,1], M0)), 36 | ?_assertEqual({ok, M3}, uef_maps:find_nested([0,1,2], M0)), 37 | ?_assertEqual({ok, Value}, uef_maps:find_nested([0,1,2,3], M0)), 38 | % find_nested/2 test with 'error' 39 | ?_assertEqual(error, uef_maps:find_nested([a], #{})), 40 | ?_assertEqual(error, uef_maps:find_nested([-1], M0)), 41 | ?_assertEqual(error, uef_maps:find_nested([-1, 1], M0)), 42 | ?_assertEqual(error, uef_maps:find_nested([1, -1], M0)), 43 | ?_assertEqual(error, uef_maps:find_nested([0,1,2,-3], M0)), 44 | % find_nested/2 test with exception 45 | ?_assertError({badmap, BadMap}, uef_maps:find_nested([0], BadMap)), 46 | ?_assertError({badmap, BadMap}, uef_maps:find_nested([0, 1], #{0 => BadMap})), 47 | ?_assertError({badmap, BadMap}, uef_maps:find_nested([0, 1, 2], #{0 => #{1 => BadMap}})), 48 | ?_assertError({badmap, Value}, uef_maps:find_nested([0,1,2,3,4], M0)), 49 | ?_assertError({badmap, Value}, uef_maps:find_nested([0,1,2,3,4,5], M0)), 50 | ?_assertError({badlist, BadList}, uef_maps:find_nested(BadList, #{a => 1})), 51 | 52 | % get_nested/2 test with Value 53 | ?_assertEqual(M1, uef_maps:get_nested([0], M0)), 54 | ?_assertEqual(M2, uef_maps:get_nested([0,1], M0)), 55 | ?_assertEqual(M3, uef_maps:get_nested([0,1,2], M0)), 56 | ?_assertEqual(Value, uef_maps:get_nested([0,1,2,3], M0)), 57 | % get_nested/2 test with exception 58 | ?_assertError({badkeys, [a]}, uef_maps:get_nested([a], M0)), 59 | ?_assertError({badkeys, [0,-1]}, uef_maps:get_nested([0,-1], M0)), 60 | ?_assertError({badkeys, [0,-1,2]}, uef_maps:get_nested([0,-1,2], M0)), 61 | ?_assertError({badkeys, [0,1,2,-3]}, uef_maps:get_nested([0,1,2,-3], M0)), 62 | ?_assertError({badmap, Value}, uef_maps:get_nested([0,1,2,3,4], M0)), 63 | ?_assertError({badmap, Value}, uef_maps:get_nested([0,1,2,3,4,5], M0)), 64 | ?_assertError({badlist, BadList}, uef_maps:get_nested(BadList, #{a => 1})), 65 | 66 | % get_nested/3 test with Value and Default 67 | ?_assertEqual(M1, uef_maps:get_nested([0], M0, Default)), 68 | ?_assertEqual(M2, uef_maps:get_nested([0,1], M0, Default)), 69 | ?_assertEqual(M3, uef_maps:get_nested([0,1,2], M0, Default)), 70 | ?_assertEqual(Value, uef_maps:get_nested([0,1,2,3], M0, Default)), 71 | ?_assertEqual(Default, uef_maps:get_nested([-1], M0, Default)), 72 | ?_assertEqual(Default, uef_maps:get_nested([0,-1], M0, Default)), 73 | ?_assertEqual(Default, uef_maps:get_nested([0,1,-2], M0, Default)), 74 | ?_assertEqual(Default, uef_maps:get_nested([0,1,2,-3], M0, Default)), 75 | ?_assertEqual(Default, uef_maps:get_nested([0,1,2,3,4], M0, Default)), 76 | ?_assertEqual(Default, uef_maps:get_nested([0,1,2,3,4,5], M0, Default)), 77 | % get_nested/3 test with exception 78 | ?_assertError({badmap, BadMap}, uef_maps:get_nested([a], BadMap, Default)), 79 | ?_assertError({badlist, BadList}, uef_maps:get_nested(BadList, #{}, Default)) 80 | ]. 81 | 82 | new_nested_test_() -> 83 | [ 84 | ?_assertEqual(#{}, uef_maps:new_nested([])), 85 | ?_assertEqual(#{}, uef_maps:new_nested([], #{})), 86 | ?_assertEqual(#{}, uef_maps:new_nested([], aaa)), 87 | ?_assertEqual(uef_maps:new_nested([]), uef_maps:new_nested([], #{})), 88 | ?_assertEqual(#{1 => #{}}, uef_maps:new_nested([1])), 89 | ?_assertEqual(#{1 => #{2 => #{}}}, uef_maps:new_nested([1,2])), 90 | ?_assertEqual(#{1 => #{2 => #{3 => #{}}}}, uef_maps:new_nested([1,2,3])), 91 | ?_assertEqual(#{1 => value}, uef_maps:new_nested([1], value)), 92 | ?_assertEqual(#{1 => #{2 => value}}, uef_maps:new_nested([1,2], value)), 93 | ?_assertEqual(#{1 => #{2 => #{3 => value}}}, uef_maps:new_nested([1,2,3], value)), 94 | ?_assertEqual(uef_maps:new_nested([]), uef_maps:new_nested([], #{})), 95 | ?_assertEqual(uef_maps:new_nested([1]), uef_maps:new_nested([1], #{})), 96 | ?_assertEqual(uef_maps:new_nested([1,2]), uef_maps:new_nested([1,2], #{})), 97 | ?_assertEqual(uef_maps:new_nested([1,2,3]), uef_maps:new_nested([1,2,3], #{})), 98 | ?_assertError({badlist, bad_list}, uef_maps:new_nested(bad_list)), 99 | ?_assertError({badlist, bad_list}, uef_maps:new_nested(bad_list, value)) 100 | ]. 101 | 102 | is_key_nested_test_() -> 103 | Value = value, 104 | M3 = #{3 => Value}, 105 | M2 = #{2 => M3}, 106 | M1 = #{1 => M2}, 107 | M0 = #{0 => M1}, 108 | BadMap = bad_map, 109 | BadList = bad_list, 110 | [ 111 | ?_assertEqual(true, uef_maps:is_key_nested([0], M0)), 112 | ?_assertEqual(true, uef_maps:is_key_nested([0,1], M0)), 113 | ?_assertEqual(true, uef_maps:is_key_nested([0,1,2], M0)), 114 | ?_assertEqual(true, uef_maps:is_key_nested([0,1,2,3], M0)), 115 | ?_assertEqual(false, uef_maps:is_key_nested([], M0)), 116 | ?_assertEqual(false, uef_maps:is_key_nested([-1], M0)), 117 | ?_assertEqual(false, uef_maps:is_key_nested([0,-1], M0)), 118 | ?_assertEqual(false, uef_maps:is_key_nested([0,1,-2], M0)), 119 | ?_assertEqual(false, uef_maps:is_key_nested([0,1,2,-3], M0)), 120 | ?_assertEqual(false, uef_maps:is_key_nested([0,-1,2,3], M0)), 121 | ?_assertEqual(false, uef_maps:is_key_nested([0,1,-2,3], M0)), 122 | ?_assertEqual(false, uef_maps:is_key_nested([0,1,2,3,4], M0)), 123 | ?_assertEqual(false, uef_maps:is_key_nested([0,1,2,3,4,5], M0)), 124 | ?_assertError({badlist, BadList}, uef_maps:is_key_nested(BadList, #{})), 125 | ?_assertError({badmap, BadMap}, uef_maps:is_key_nested([1], BadMap)) 126 | ]. 127 | 128 | put_nested_test_() -> 129 | Map1 = #{1 => #{2 => #{3 => val3}}}, 130 | NewVal = new_value, 131 | BadMap = bad_map, 132 | BadList = bad_list, 133 | [ 134 | ?_assertEqual(Map1, uef_maps:put_nested([], NewVal, Map1)), 135 | ?_assertEqual(#{1 => NewVal}, uef_maps:put_nested([1], NewVal, Map1)), 136 | ?_assertEqual(#{1 => #{2 => NewVal}}, uef_maps:put_nested([1,2], NewVal, Map1)), 137 | ?_assertEqual(#{1 => #{2 => #{3 => NewVal}}}, uef_maps:put_nested([1,2,3], NewVal, Map1)), 138 | ?_assertEqual(#{1 => #{2 => #{3 => val3, -3 => NewVal}}}, uef_maps:put_nested([1,2,-3], NewVal, Map1)), 139 | ?_assertEqual(#{1 => #{2 => #{3 => #{4 => NewVal}}}}, uef_maps:put_nested([1,2,3,4], NewVal, Map1)), 140 | ?_assertEqual(#{1 => #{2 => #{3 => val3}}, -1 => NewVal}, uef_maps:put_nested([-1], NewVal, Map1)), 141 | ?_assertEqual(#{1 => #{2 => #{3 => val3}, -2 => NewVal}}, uef_maps:put_nested([1,-2], NewVal, Map1)), 142 | ?_assertEqual(#{1 => #{2 => #{3 => val3, -3 => NewVal}}}, uef_maps:put_nested([1,2,-3], NewVal, Map1)), 143 | ?_assertEqual(#{1 => #{2 => #{3 => #{-4 => NewVal}}}}, uef_maps:put_nested([1,2,3,-4], NewVal, Map1)), 144 | ?_assertError({badlist, BadList}, uef_maps:put_nested(BadList, NewVal, Map1)), 145 | ?_assertError({badmap, BadMap}, uef_maps:put_nested([], NewVal, BadMap)) 146 | ]. 147 | 148 | update_nested_test_() -> 149 | Map1 = #{1 => #{2 => #{3 => val3}}}, 150 | NewVal = new_value, 151 | BadMap = bad_map, 152 | BadList = bad_list, 153 | [ 154 | ?_assertEqual(Map1, uef_maps:update_nested([], NewVal, Map1)), 155 | ?_assertEqual(#{1 => NewVal}, uef_maps:update_nested([1], NewVal, Map1)), 156 | ?_assertEqual(#{1 => #{2 => NewVal}}, uef_maps:update_nested([1,2], NewVal, Map1)), 157 | ?_assertEqual(#{1 => #{2 => #{3 => NewVal}}}, uef_maps:update_nested([1,2,3], NewVal, Map1)), 158 | ?_assertError({badkey, -3}, uef_maps:update_nested([1,2,-3], NewVal, Map1)), 159 | ?_assertError({badkey, 4}, uef_maps:update_nested([1,2,3,4], NewVal, Map1)), 160 | ?_assertError({badkey, 4}, uef_maps:update_nested([1,2,3,4,5], NewVal, Map1)), 161 | ?_assertError({badkey, -1}, uef_maps:update_nested([-1], NewVal, Map1)), 162 | ?_assertError({badkey, -2}, uef_maps:update_nested([1,-2], NewVal, Map1)), 163 | ?_assertError({badkey, -3}, uef_maps:update_nested([1,2,-3], NewVal, Map1)), 164 | ?_assertError({badkey, -4}, uef_maps:update_nested([1,2,3,-4], NewVal, Map1)), 165 | ?_assertError({badlist, BadList}, uef_maps:update_nested(BadList, NewVal, Map1)), 166 | ?_assertError({badmap, BadMap}, uef_maps:update_nested([], NewVal, BadMap)) 167 | ]. 168 | 169 | remove_nested_test_() -> 170 | Map1 = #{1 => #{2 => #{3 => val3}}}, 171 | BadMap = bad_map, 172 | BadList = bad_list, 173 | [ 174 | ?_assertEqual(Map1, uef_maps:remove_nested([], Map1)), 175 | ?_assertEqual(#{}, uef_maps:remove_nested([1], Map1)), 176 | ?_assertEqual(#{1 => #{}}, uef_maps:remove_nested([1,2], Map1)), 177 | ?_assertEqual(#{1 => #{2 => #{}}}, uef_maps:remove_nested([1,2,3], Map1)), 178 | ?_assertEqual(Map1, uef_maps:remove_nested([-1], Map1)), 179 | ?_assertEqual(Map1, uef_maps:remove_nested([1,-2], Map1)), 180 | ?_assertEqual(Map1, uef_maps:remove_nested([1,2,-3], Map1)), 181 | ?_assertEqual(Map1, uef_maps:remove_nested([1,2,3,4], Map1)), 182 | ?_assertEqual(Map1, uef_maps:remove_nested([1,2,3,4,5], Map1)), 183 | ?_assertError({badlist, BadList}, uef_maps:remove_nested(BadList, Map1)), 184 | ?_assertError({badmap, BadMap}, uef_maps:remove_nested([], BadMap)) 185 | ]. 186 | 187 | take_nested_test_() -> 188 | Map1 = #{1 => #{2 => #{3 => val3}}}, 189 | BadMap = bad_map, 190 | BadList = bad_list, 191 | [ 192 | ?_assertEqual(error, uef_maps:take_nested([], Map1)), 193 | ?_assertEqual({ #{2 => #{3 => val3}}, #{} }, uef_maps:take_nested([1], Map1)), 194 | ?_assertEqual({ #{3 => val3}, #{1 => #{}} }, uef_maps:take_nested([1,2], Map1)), 195 | ?_assertEqual({ val3, #{1 => #{2 => #{}}} }, uef_maps:take_nested([1,2,3], Map1)), 196 | ?_assertEqual(error, uef_maps:take_nested([-1], Map1)), 197 | ?_assertEqual(error, uef_maps:take_nested([1,-2], Map1)), 198 | ?_assertEqual(error, uef_maps:take_nested([1,2,-3], Map1)), 199 | ?_assertEqual(error, uef_maps:take_nested([1,2,3,4], Map1)), 200 | ?_assertEqual(error, uef_maps:take_nested([1,2,3,4,5], Map1)), 201 | ?_assertError({badlist, BadList}, uef_maps:take_nested(BadList, Map1)), 202 | ?_assertError({badmap, BadMap}, uef_maps:take_nested([], BadMap)) 203 | ]. 204 | 205 | delete_nested_test_() -> 206 | Map1 = #{1 => #{2 => #{3 => val3}}}, 207 | BadMap = bad_map, 208 | BadList = bad_list, 209 | [ 210 | ?_assertEqual({error, empty_keys}, uef_maps:delete_nested([], Map1)), 211 | ?_assertEqual({ok, #{}}, uef_maps:delete_nested([1], Map1)), 212 | ?_assertEqual({ok, #{1 => #{}}}, uef_maps:delete_nested([1,2], Map1)), 213 | ?_assertEqual({ ok, #{1 => #{2 => #{}}} }, uef_maps:delete_nested([1,2,3], Map1)), 214 | ?_assertEqual({error, {badkey, -1}}, uef_maps:delete_nested([-1], Map1)), 215 | ?_assertEqual({error, {badkey, -2}}, uef_maps:delete_nested([1,-2], Map1)), 216 | ?_assertEqual({error, {badkey, -3}}, uef_maps:delete_nested([1,2,-3], Map1)), 217 | ?_assertEqual({error, {badkey, 4}}, uef_maps:delete_nested([1,2,3,4], Map1)), 218 | ?_assertEqual({error, {badkey, 4}}, uef_maps:delete_nested([1,2,3,4,5], Map1)), 219 | ?_assertError({badlist, BadList}, uef_maps:delete_nested(BadList, Map1)), 220 | ?_assertError({badmap, BadMap}, uef_maps:delete_nested([], BadMap)) 221 | ]. 222 | -------------------------------------------------------------------------------- /test/uef_num_tests.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2019-2024, Sergei Semichev . All Rights Reserved. 2 | %% 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | 15 | -module(uef_num_tests). 16 | 17 | -include_lib("eunit/include/eunit.hrl"). 18 | 19 | %%%------------------------------------------------------------------------------ 20 | %%% Tests 21 | %%%------------------------------------------------------------------------------ 22 | 23 | round_number_test_() -> 24 | [ 25 | ?_assertEqual(1.0, uef_num:round_price(1)), 26 | ?_assertEqual(1.01, uef_num:round_price(1.01)), 27 | ?_assertEqual(1.01, uef_num:round_price(1.015)), 28 | ?_assertEqual(1.02, uef_num:round_price(1.025)), 29 | ?_assertEqual(1.02, uef_num:round_price(1.0155)), 30 | ?_assertEqual(1.015, uef_num:round_number(1.015, 3)), 31 | ?_assertEqual(2.0, uef_num:round_number(1.9999, 1)), 32 | ?_assertEqual(2.0, uef_num:round_number(1.9999, 2)), 33 | ?_assertEqual(1.9999, uef_num:round_number(1.9999, 4)), 34 | ?_assertEqual(-1.9999, uef_num:round_number(-1.9999, 4)), 35 | ?_assertEqual(-2.0, uef_num:round_number(-1.9999, 3)), 36 | ?_assertEqual(10000.0, uef_num:round_number(9999.999999, 5)) 37 | ]. 38 | 39 | 40 | popcount_test_() -> 41 | [ 42 | ?_assertEqual(0, uef_num:popcount(0)), 43 | ?_assertEqual(3, uef_num:popcount(7)), 44 | ?_assertEqual(1, uef_num:popcount(8)), 45 | ?_assertEqual(1, uef_num:popcount(8)), 46 | ?_assertEqual(1, uef_num:popcount(2#0000000000000000000000000000000000000000000000000000000000000001)), 47 | ?_assertEqual(1, uef_num:popcount(2#1000000000000000000000000000000000000000000000000000000000000000)), 48 | ?_assertEqual(8, uef_num:popcount(2#0000000100000001000000010000000100000001000000010000000100000001)), 49 | ?_assertEqual(8, uef_num:popcount(2#0000000000000000000000000000000000000000000000000000000011111111)), 50 | ?_assertEqual(16, uef_num:popcount(2#1111111100000000000000000000000000000000000000000000000011111111)), 51 | ?_assertEqual(64, uef_num:popcount(2#1111111111111111111111111111111111111111111111111111111111111111)), 52 | ?_assertError({badarg, -1}, uef_num:popcount(-1)), 53 | ?_assertError({badarg, 1.0}, uef_num:popcount(1.0)), 54 | ?_assertError({badarg, 0.0}, uef_num:popcount(0.0)) 55 | ]. 56 | 57 | 58 | msb_pos_test_() -> 59 | [ 60 | ?_assertEqual(1, uef_num:msb_pos(2#1)), 61 | ?_assertEqual(1, uef_num:msb_pos(2#01)), 62 | ?_assertEqual(2, uef_num:msb_pos(2#10)), 63 | ?_assertEqual(3, uef_num:msb_pos(2#100)), 64 | ?_assertEqual(3, uef_num:msb_pos(2#0100)), 65 | ?_assertEqual(3, uef_num:msb_pos(2#111)), 66 | ?_assertEqual(3, uef_num:msb_pos(2#0111)), 67 | ?_assertEqual(1, uef_num:msb_pos(2#0000000000000000000000000000000000000000000000000000000000000001)), 68 | ?_assertEqual(64, uef_num:msb_pos(2#1000000000000000000000000000000000000000000000000000000000000000)), 69 | ?_assertEqual(57, uef_num:msb_pos(2#0000000100000001000000010000000100000001000000010000000100000001)), 70 | ?_assertEqual(8, uef_num:msb_pos(2#0000000000000000000000000000000000000000000000000000000011111111)), 71 | ?_assertEqual(64, uef_num:msb_pos(2#1111111100000000000000000000000000000000000000000000000011111111)), 72 | ?_assertEqual(64, uef_num:msb_pos(2#1111111111111111111111111111111111111111111111111111111111111111)), 73 | ?_assertError({badarg, 0}, uef_num:msb_pos(0)), 74 | ?_assertError({badarg, -1}, uef_num:msb_pos(-1)), 75 | ?_assertError({badarg, 1.0}, uef_num:msb_pos(1.0)), 76 | ?_assertError({badarg, 0.0}, uef_num:msb_pos(0.0)) 77 | ]. 78 | 79 | lsb_pos_test_() -> 80 | [ 81 | ?_assertEqual(1, uef_num:lsb_pos(1)), 82 | ?_assertEqual(1, uef_num:lsb_pos(2#01)), 83 | ?_assertEqual(2, uef_num:lsb_pos(2#10)), 84 | ?_assertEqual(3, uef_num:lsb_pos(2#100)), 85 | ?_assertEqual(3, uef_num:lsb_pos(2#0100)), 86 | ?_assertEqual(1, uef_num:lsb_pos(2#111)), 87 | ?_assertEqual(1, uef_num:lsb_pos(2#0111)), 88 | ?_assertEqual(4, uef_num:lsb_pos(2#0101000)), 89 | ?_assertEqual(1, uef_num:lsb_pos(2#0000000000000000000000000000000000000000000000000000000000000001)), 90 | ?_assertEqual(64, uef_num:lsb_pos(2#1000000000000000000000000000000000000000000000000000000000000000)), 91 | ?_assertEqual(9, uef_num:lsb_pos(2#0000000100000001000000010000000100000001000000010000000100000000)), 92 | ?_assertEqual(1, uef_num:lsb_pos(2#0000000000000000000000000000000000000000000000000000000011111111)), 93 | ?_assertEqual(1, uef_num:lsb_pos(2#1111111100000000000000000000000000000000000000000000000011111111)), 94 | ?_assertEqual(1, uef_num:lsb_pos(2#1111111111111111111111111111111111111111111111111111111111111111)), 95 | ?_assertError({badarg, 0}, uef_num:lsb_pos(0)), 96 | ?_assertError({badarg, -1}, uef_num:lsb_pos(-1)), 97 | ?_assertError({badarg, 1.0}, uef_num:lsb_pos(1.0)), 98 | ?_assertError({badarg, 0.0}, uef_num:lsb_pos(0.0)) 99 | ]. 100 | 101 | ctz_test_() -> 102 | [ 103 | ?_assertEqual(0, uef_num:ctz(1)), 104 | ?_assertEqual(0, uef_num:ctz(2#01)), 105 | ?_assertEqual(1, uef_num:ctz(2#10)), 106 | ?_assertEqual(2, uef_num:ctz(2#100)), 107 | ?_assertEqual(2, uef_num:ctz(2#0100)), 108 | ?_assertEqual(0, uef_num:ctz(2#111)), 109 | ?_assertEqual(0, uef_num:ctz(2#0111)), 110 | ?_assertEqual(3, uef_num:ctz(2#0101000)), 111 | ?_assertEqual(0, uef_num:ctz(2#0000000000000000000000000000000000000000000000000000000000000001)), 112 | ?_assertEqual(63, uef_num:ctz(2#1000000000000000000000000000000000000000000000000000000000000000)), 113 | ?_assertEqual(8, uef_num:ctz(2#0000000100000001000000010000000100000001000000010000000100000000)), 114 | ?_assertEqual(0, uef_num:ctz(2#0000000000000000000000000000000000000000000000000000000011111111)), 115 | ?_assertEqual(0, uef_num:ctz(2#1111111100000000000000000000000000000000000000000000000011111111)), 116 | ?_assertEqual(0, uef_num:ctz(2#1111111111111111111111111111111111111111111111111111111111111111)), 117 | ?_assertError({badarg, 0}, uef_num:ctz(0)), 118 | ?_assertError({badarg, -1}, uef_num:ctz(-1)), 119 | ?_assertError({badarg, 1.0}, uef_num:ctz(1.0)), 120 | ?_assertError({badarg, 0.0}, uef_num:ctz(0.0)) 121 | ]. 122 | -------------------------------------------------------------------------------- /test/uef_time_tests.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2019-2024, Sergei Semichev . All Rights Reserved. 2 | %% 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | 15 | -module(uef_time_tests). 16 | 17 | -include_lib("eunit/include/eunit.hrl"). 18 | 19 | %%%------------------------------------------------------------------------------ 20 | %%% Tests 21 | %%%------------------------------------------------------------------------------ 22 | 23 | add_seconds_test_() -> 24 | [ 25 | ?_assertEqual({{0,1,1}, {0,0,0}}, uef_time:add_seconds({0, 1, 1}, 0)), 26 | ?_assertEqual({{0,1,1}, {0,0,1}}, uef_time:add_seconds({0, 1, 1}, 1)), 27 | ?_assertEqual({{2001,1,1}, {0,0,0}}, uef_time:add_seconds({{2000,12,31}, {23,59,59}}, 1)), 28 | ?_assertEqual({{2000,2,1}, {0,0,0}}, uef_time:add_seconds({{2000,1,31}, {23,59,59}}, 1)), 29 | ?_assertEqual({{1999,12,31}, {23,59,59}}, uef_time:add_seconds({2000,1,1}, -1)), 30 | ?_assertEqual(uef_time:add_seconds(1), uef_time:add_seconds(erlang:localtime(), 1)), 31 | ?_assertMatch({{_,_,_}, {_,_,_}}, uef_time:add_seconds(1)), 32 | ?_assertMatch({{_,_,_}, {_,_,_}}, uef_time:add_seconds(erlang:localtime(), 1)), 33 | ?_assertMatch({{_,_,_}, {_,_,_}}, uef_time:add_seconds({1,1,1}, 1)) 34 | ]. 35 | 36 | add_minutes_test_() -> 37 | Date1 = erlang:date(), 38 | [ 39 | ?_assertEqual({{0,1,1}, {0,0,0}}, uef_time:add_minutes({0, 1, 1}, 0)), 40 | ?_assertEqual({{0,1,1}, {0,1,0}}, uef_time:add_minutes({0, 1, 1}, 1)), 41 | ?_assertEqual({{2001,1,1}, {0,0,0}}, uef_time:add_minutes({{2000,12,31}, {23,59,0}}, 1)), 42 | ?_assertEqual({{1999,12,31}, {23,59,0}}, uef_time:add_minutes({2000,1,1}, -1)), 43 | ?_assertEqual(uef_time:add_seconds(Date1, 60), uef_time:add_minutes(Date1, 1)), 44 | ?_assertEqual(uef_time:add_seconds(Date1, -60), uef_time:add_minutes(Date1, -1)), 45 | ?_assertEqual(uef_time:add_minutes(1), uef_time:add_minutes(erlang:localtime(), 1)), 46 | ?_assertMatch({{_,_,_}, {_,_,_}}, uef_time:add_minutes(1)), 47 | ?_assertMatch({{_,_,_}, {_,_,_}}, uef_time:add_minutes(erlang:localtime(), 1)), 48 | ?_assertMatch({{_,_,_}, {_,_,_}}, uef_time:add_minutes(Date1, 1)) 49 | ]. 50 | 51 | add_hours_test_() -> 52 | Date1 = erlang:date(), 53 | [ 54 | ?_assertEqual({{1999,12,31}, {23,0,0}}, uef_time:add_hours({2000,1,1}, -1)), 55 | ?_assertEqual({{2001,1,1}, {0,0,0}}, uef_time:add_hours({{2000,12,31}, {23,0,0}}, 1)), 56 | ?_assertEqual(uef_time:add_hours(1), uef_time:add_hours(erlang:localtime(), 1)), 57 | ?_assertEqual(uef_time:add_seconds(Date1, 3600), uef_time:add_hours(Date1, 1)), 58 | ?_assertEqual(uef_time:add_seconds(Date1, -3600), uef_time:add_hours(Date1, -1)), 59 | ?_assertEqual(uef_time:add_minutes(Date1, 60), uef_time:add_hours(Date1, 1)), 60 | ?_assertEqual(uef_time:add_minutes(Date1, -60), uef_time:add_hours(Date1, -1)), 61 | ?_assertMatch({{_,_,_}, {_,_,_}}, uef_time:add_hours(1)), 62 | ?_assertMatch({{_,_,_}, {_,_,_}}, uef_time:add_hours(erlang:localtime(), 1)), 63 | ?_assertMatch({{_,_,_}, {_,_,_}}, uef_time:add_hours(Date1, 1)) 64 | ]. 65 | 66 | add_days_test_() -> 67 | Date1 = erlang:date(), 68 | [ 69 | ?_assertEqual({1999,12,31}, uef_time:add_days({2000,1,1}, -1)), 70 | ?_assertEqual({{2001,1,1}, {0,0,0}}, uef_time:add_days({{2000,12,31}, {0,0,0}}, 1)), 71 | ?_assertEqual(uef_time:add_days(1), uef_time:add_days(erlang:localtime(), 1)), 72 | ?_assertEqual(uef_time:add_seconds(Date1, 86400), uef_time:add_days({Date1, {0,0,0}}, 1)), 73 | ?_assertEqual(uef_time:add_seconds(Date1, -86400), uef_time:add_days({Date1, {0,0,0}}, -1)), 74 | ?_assertEqual(uef_time:add_hours(Date1, 24), uef_time:add_days({Date1, {0,0,0}}, 1)), 75 | ?_assertEqual(uef_time:add_hours(Date1, -24), uef_time:add_days({Date1, {0,0,0}}, -1)), 76 | ?_assertMatch({{_,_,_}, {_,_,_}}, uef_time:add_days(1)), 77 | ?_assertMatch({{_,_,_}, {_,_,_}}, uef_time:add_days(erlang:localtime(), 1)), 78 | ?_assertMatch({_,_,_}, uef_time:add_days(Date1, 1)) 79 | ]. 80 | 81 | add_weeks_test_() -> 82 | Date1 = erlang:date(), 83 | [ 84 | ?_assertEqual(uef_time:add_days(7), uef_time:add_weeks(1)), 85 | ?_assertEqual(uef_time:add_days(-7), uef_time:add_weeks(-1)), 86 | ?_assertEqual(uef_time:add_days(Date1, 7), uef_time:add_weeks(Date1, 1)), 87 | ?_assertEqual(uef_time:add_days(Date1, -7), uef_time:add_weeks(Date1, -1)), 88 | ?_assertEqual({2019, 1, 8}, uef_time:add_weeks({2019, 1, 1}, 1)), 89 | ?_assertEqual({2018, 12, 25}, uef_time:add_weeks({2019, 1, 1}, -1)), 90 | ?_assertMatch({{_,_,_}, {_,_,_}}, uef_time:add_weeks(1)), 91 | ?_assertMatch({{_,_,_}, {_,_,_}}, uef_time:add_weeks(erlang:localtime(), 1)), 92 | ?_assertMatch({_,_,_}, uef_time:add_weeks(Date1, 1)) 93 | ]. 94 | 95 | add_months_test_() -> 96 | Date1 = erlang:date(), 97 | {Year1, _, _} = Date1, 98 | [ 99 | ?_assertEqual({2016, 2, 29}, uef_time:add_months({2016, 1, 31}, 1)), % leap year 100 | ?_assertEqual({2017, 2, 28}, uef_time:add_months({2017, 1, 31}, 1)), % non-leap year 101 | ?_assertEqual({Year1 - 1, 12, 31}, uef_time:add_months({Year1, 1, 31}, -1)), 102 | ?_assertEqual({Year1 + 1, 1, 31}, uef_time:add_months({Year1, 1, 31}, 12)), 103 | ?_assertEqual({Year1 - 1, 1, 31}, uef_time:add_months({Year1, 1, 31}, -12)), 104 | ?_assertEqual({Year1, 2, 1}, uef_time:add_months({Year1, 1, 1}, 1)), 105 | ?_assertEqual({Year1 - 1, 12, 1}, uef_time:add_months({Year1, 1, 1}, -1)), 106 | ?_assertMatch({{_,_,_}, {_,_,_}}, uef_time:add_months(1)), 107 | ?_assertMatch({{_,_,_}, {_,_,_}}, uef_time:add_months(erlang:localtime(), 1)), 108 | ?_assertMatch({_,_,_}, uef_time:add_months(Date1, 1)) 109 | ]. 110 | 111 | add_years_test_() -> 112 | Date1 = erlang:date(), 113 | {Year1, _, _} = Date1, 114 | [ 115 | ?_assertEqual(uef_time:add_months(12), uef_time:add_years(1)), 116 | ?_assertEqual(uef_time:add_months(-12), uef_time:add_years(-1)), 117 | ?_assertEqual(uef_time:add_months(Date1, 12), uef_time:add_years(Date1, 1)), 118 | ?_assertEqual(uef_time:add_months(Date1, -12), uef_time:add_years(Date1, -1)), 119 | ?_assertEqual({Year1 + 1, 1, 1}, uef_time:add_years({Year1, 1, 1}, 1)), 120 | ?_assertEqual({Year1 - 1, 1, 1}, uef_time:add_years({Year1, 1, 1}, -1)), 121 | ?_assertMatch({{_,_,_}, {_,_,_}}, uef_time:add_years(1)), 122 | ?_assertMatch({{_,_,_}, {_,_,_}}, uef_time:add_years(erlang:localtime(), 1)), 123 | ?_assertMatch({_,_,_}, uef_time:add_years(Date1, 1)) 124 | ]. 125 | 126 | add_time_test_() -> 127 | Date1 = erlang:date(), 128 | DateTime1 = erlang:localtime(), 129 | AllPeriods = [{1, year}, {1, month}, {1, week}, {1, day}, {1, hour}, {1, minute}, {1, second}], 130 | [ 131 | ?_assertEqual(uef_time:add_time(AllPeriods), uef_time:add_time(erlang:localtime(), AllPeriods)), 132 | 133 | ?_assertEqual(uef_time:add_time(Date1, [{1, sec}]), uef_time:add_time(Date1, [{1, seconds}])), 134 | ?_assertEqual(uef_time:add_time(Date1, [{1, second}]), uef_time:add_time(Date1, [{1, seconds}])), 135 | ?_assertEqual(uef_time:add_time(Date1, [{1, min}]), uef_time:add_time(Date1, [{1, minutes}])), 136 | ?_assertEqual(uef_time:add_time(Date1, [{1, minute}]), uef_time:add_time(Date1, [{1, minutes}])), 137 | ?_assertEqual(uef_time:add_time(Date1, [{1, hrs}]), uef_time:add_time(Date1, [{1, hours}])), 138 | ?_assertEqual(uef_time:add_time(Date1, [{1, hour}]), uef_time:add_time(Date1, [{1, hours}])), 139 | ?_assertEqual(uef_time:add_time(Date1, [{1, day}]), uef_time:add_time(Date1, [{1, days}])), 140 | ?_assertEqual(uef_time:add_time(Date1, [{1, week}]), uef_time:add_time(Date1, [{1, weeks}])), 141 | ?_assertEqual(uef_time:add_time(Date1, [{1, month}]), uef_time:add_time(Date1, [{1, months}])), 142 | ?_assertEqual(uef_time:add_time(Date1, [{1, year}]), uef_time:add_time(Date1, [{1, years}])), 143 | 144 | ?_assertEqual(uef_time:add_time(Date1, [{seconds, 1}]), uef_time:add_time(Date1, [{1, seconds}])), 145 | ?_assertEqual(uef_time:add_time(Date1, [{minutes, 1}]), uef_time:add_time(Date1, [{1, minutes}])), 146 | ?_assertEqual(uef_time:add_time(Date1, [{hours, 1}]), uef_time:add_time(Date1, [{1, hours}])), 147 | ?_assertEqual(uef_time:add_time(Date1, [{days, 1}]), uef_time:add_time(Date1, [{1, days}])), 148 | ?_assertEqual(uef_time:add_time(Date1, [{weeks, 1}]), uef_time:add_time(Date1, [{1, weeks}])), 149 | ?_assertEqual(uef_time:add_time(Date1, [{months, 1}]), uef_time:add_time(Date1, [{1, months}])), 150 | ?_assertEqual(uef_time:add_time(Date1, [{years, 1}]), uef_time:add_time(Date1, [{1, years}])), 151 | 152 | ?_assertEqual(uef_time:add_seconds(Date1, 1), uef_time:add_time(Date1, [{1, seconds}])), 153 | ?_assertEqual(uef_time:add_seconds(DateTime1, 1), uef_time:add_time(DateTime1, [{1, seconds}])), 154 | ?_assertEqual(uef_time:add_minutes(Date1, 1), uef_time:add_time(Date1, [{1, minutes}])), 155 | ?_assertEqual(uef_time:add_minutes(DateTime1, 1), uef_time:add_time(DateTime1, [{1, minutes}])), 156 | ?_assertEqual(uef_time:add_hours(Date1, 1), uef_time:add_time(Date1, [{1, hours}])), 157 | ?_assertEqual(uef_time:add_hours(DateTime1, 1), uef_time:add_time(DateTime1, [{1, hours}])), 158 | ?_assertEqual(uef_time:add_days(Date1, 1), uef_time:add_time(Date1, [{1, days}])), 159 | ?_assertEqual(uef_time:add_days(DateTime1, 1), uef_time:add_time(DateTime1, [{1, days}])), 160 | ?_assertEqual(uef_time:add_weeks(Date1, 1), uef_time:add_time(Date1, [{1, weeks}])), 161 | ?_assertEqual(uef_time:add_weeks(DateTime1, 1), uef_time:add_time(DateTime1, [{1, weeks}])), 162 | ?_assertEqual(uef_time:add_months(Date1, 1), uef_time:add_time(Date1, [{1, months}])), 163 | ?_assertEqual(uef_time:add_months(DateTime1, 1), uef_time:add_time(DateTime1, [{1, months}])), 164 | ?_assertEqual(uef_time:add_years(Date1, 1), uef_time:add_time(Date1, [{1, years}])), 165 | ?_assertEqual(uef_time:add_years(DateTime1, 1), uef_time:add_time(DateTime1, [{1, years}])), 166 | 167 | ?_assertException(error, {badarg, some_period}, uef_time:add_time(DateTime1, [{1, some_period}])), 168 | ?_assertException(error, {badarg, {1.0, year}}, uef_time:add_time(DateTime1, [{1.0, year}])) 169 | ]. 170 | 171 | today_test_() -> 172 | [ 173 | ?_assertEqual(erlang:date(), uef_time:today()), 174 | ?_assertMatch({_,_,_}, uef_time:today()) 175 | ]. 176 | 177 | tomorrow_test_() -> 178 | [ 179 | ?_assertEqual(uef_time:add_days(uef_time:today(), 1), uef_time:tomorrow()), 180 | ?_assertMatch({_,_,_}, uef_time:tomorrow()) 181 | ]. 182 | 183 | yesterday_test_() -> 184 | [ 185 | ?_assertEqual(uef_time:add_days(uef_time:today(), -1), uef_time:yesterday()), 186 | ?_assertMatch({_,_,_}, uef_time:yesterday()) 187 | ]. 188 | 189 | days_diff_1_test_() -> 190 | Date = {2021, 12, 31}, 191 | [ 192 | ?_assertEqual(uef_time:days_diff(Date), uef_time:days_diff(erlang:date(), Date)) 193 | ]. 194 | 195 | days_diff_2_test_() -> 196 | [ 197 | ?_assertEqual(1, uef_time:days_diff({2018, 12, 31}, {2019, 1, 1})), 198 | ?_assertEqual(-1, uef_time:days_diff({2019, 1, 1}, {2018, 12, 31})), 199 | ?_assertEqual(0, uef_time:days_diff({2019, 4, 23}, {2019, 4, 23})) 200 | ]. 201 | 202 | seconds_diff_1_test_() -> 203 | DateTime = {{2021, 12, 31}, {0, 0, 0}}, 204 | [ 205 | ?_assertEqual(uef_time:seconds_diff(DateTime), uef_time:seconds_diff(erlang:localtime(), DateTime)) 206 | ]. 207 | 208 | seconds_diff_2_test_() -> 209 | Date = erlang:date(), 210 | [ 211 | ?_assertEqual(0, uef_time:seconds_diff({Date, {17, 0, 0}}, {Date, {17, 0, 0}})), 212 | ?_assertEqual(1, uef_time:seconds_diff({Date, {17, 0, 0}}, {Date, {17, 0, 1}})), 213 | ?_assertEqual(3600, uef_time:seconds_diff({Date, {17, 0, 0}}, {Date, {18, 0, 0}})), 214 | ?_assertEqual(-3600, uef_time:seconds_diff({Date, {18, 0, 0}}, {Date, {17, 0, 0}})), 215 | ?_assertEqual(60, uef_time:seconds_diff({Date, {17, 0, 0}}, {Date, {17, 1, 0}})) 216 | ]. 217 | 218 | unix_time_test_() -> 219 | [ 220 | ?_assertEqual(0, uef_time:unix_time({{1970,1,1}, {0,0,0}})), 221 | ?_assertEqual(1, uef_time:unix_time({{1970,1,1}, {0,0,1}})), 222 | ?_assertEqual(59, uef_time:unix_time({{1970,1,1}, {0,0,59}})), 223 | ?_assertEqual(uef_time:unix_time(), uef_time:unix_time(calendar:universal_time())) 224 | ]. 225 | --------------------------------------------------------------------------------