pybase64 1.3.2__tar.gz → 1.4.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. {pybase64-1.3.2 → pybase64-1.4.1}/MANIFEST.in +3 -0
  2. {pybase64-1.3.2/src/pybase64.egg-info → pybase64-1.4.1}/PKG-INFO +27 -5
  3. {pybase64-1.3.2 → pybase64-1.4.1}/README.rst +13 -0
  4. {pybase64-1.3.2 → pybase64-1.4.1}/base64/.github/workflows/test.yml +108 -17
  5. {pybase64-1.3.2 → pybase64-1.4.1}/base64/CMakeLists.txt +5 -1
  6. {pybase64-1.3.2 → pybase64-1.4.1}/base64/Makefile +14 -5
  7. {pybase64-1.3.2 → pybase64-1.4.1}/base64/bin/base64.c +225 -25
  8. pybase64-1.4.1/base64/bin/base64.manifest +23 -0
  9. pybase64-1.4.1/base64/bin/base64.rc +2 -0
  10. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/arch/avx/codec.c +6 -4
  11. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/arch/avx2/codec.c +6 -4
  12. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/arch/avx2/dec_loop.c +1 -1
  13. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/arch/avx2/dec_reshuffle.c +1 -1
  14. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/arch/avx2/enc_loop.c +2 -2
  15. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/arch/avx2/enc_reshuffle.c +1 -1
  16. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/arch/avx2/enc_translate.c +1 -1
  17. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/arch/avx512/codec.c +6 -4
  18. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/arch/avx512/enc_loop.c +1 -1
  19. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/arch/avx512/enc_reshuffle_translate.c +1 -1
  20. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/arch/generic/32/dec_loop.c +1 -1
  21. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/arch/generic/32/enc_loop.c +1 -1
  22. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/arch/generic/64/enc_loop.c +1 -1
  23. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/arch/generic/codec.c +4 -2
  24. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/arch/neon32/codec.c +7 -5
  25. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/arch/neon32/dec_loop.c +3 -3
  26. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/arch/neon32/enc_loop.c +2 -2
  27. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/arch/neon32/enc_reshuffle.c +1 -1
  28. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/arch/neon32/enc_translate.c +1 -1
  29. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/arch/neon64/codec.c +7 -5
  30. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/arch/neon64/enc_loop.c +1 -1
  31. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/arch/neon64/enc_reshuffle.c +1 -1
  32. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/arch/sse41/codec.c +6 -4
  33. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/arch/sse42/codec.c +6 -4
  34. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/arch/ssse3/codec.c +6 -4
  35. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/arch/ssse3/dec_loop.c +1 -1
  36. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/arch/ssse3/dec_reshuffle.c +1 -1
  37. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/arch/ssse3/enc_loop.c +1 -1
  38. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/arch/ssse3/enc_reshuffle.c +1 -1
  39. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/arch/ssse3/enc_translate.c +1 -1
  40. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/codec_choose.c +14 -5
  41. pybase64-1.4.1/base64/lib/codecs.h +57 -0
  42. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/env.h +16 -6
  43. {pybase64-1.3.2 → pybase64-1.4.1}/base64/test/CMakeLists.txt +4 -6
  44. {pybase64-1.3.2 → pybase64-1.4.1}/base64/test/Makefile +3 -1
  45. {pybase64-1.3.2 → pybase64-1.4.1}/base64/test/benchmark.c +72 -17
  46. {pybase64-1.3.2 → pybase64-1.4.1}/base64/test/ci/analysis.sh +2 -1
  47. pybase64-1.4.1/base64/test/ci/checksums.txt +1 -0
  48. {pybase64-1.3.2 → pybase64-1.4.1}/base64/test/ci/test.sh +4 -6
  49. pybase64-1.4.1/base64/test/ci//360/237/230/265/342/200/215/360/237/222/253.bin +2 -0
  50. pybase64-1.4.1/base64/test/codec_supported.c +40 -0
  51. pybase64-1.4.1/base64/test/codec_supported.h +3 -0
  52. {pybase64-1.3.2 → pybase64-1.4.1}/base64/test/test_base64.c +5 -7
  53. pybase64-1.4.1/pyproject.toml +109 -0
  54. {pybase64-1.3.2 → pybase64-1.4.1}/setup.cfg +0 -3
  55. {pybase64-1.3.2 → pybase64-1.4.1}/setup.py +58 -50
  56. {pybase64-1.3.2 → pybase64-1.4.1}/src/pybase64/__init__.py +31 -8
  57. {pybase64-1.3.2 → pybase64-1.4.1}/src/pybase64/__main__.py +63 -50
  58. {pybase64-1.3.2 → pybase64-1.4.1}/src/pybase64/_fallback.py +28 -24
  59. pybase64-1.4.1/src/pybase64/_license.pyi +1 -0
  60. {pybase64-1.3.2 → pybase64-1.4.1}/src/pybase64/_pybase64.c +135 -61
  61. pybase64-1.4.1/src/pybase64/_pybase64.pyi +16 -0
  62. pybase64-1.4.1/src/pybase64/_typing.py +35 -0
  63. pybase64-1.4.1/src/pybase64/_version.py +3 -0
  64. {pybase64-1.3.2 → pybase64-1.4.1/src/pybase64.egg-info}/PKG-INFO +27 -5
  65. {pybase64-1.3.2 → pybase64-1.4.1}/src/pybase64.egg-info/SOURCES.txt +8 -1
  66. pybase64-1.4.1/tests/test_benchmark.py +44 -0
  67. {pybase64-1.3.2 → pybase64-1.4.1}/tests/test_main.py +46 -39
  68. {pybase64-1.3.2 → pybase64-1.4.1}/tests/test_pybase64.py +144 -177
  69. pybase64-1.3.2/base64/lib/codecs.h +0 -65
  70. pybase64-1.3.2/base64/test/codec_supported.c +0 -29
  71. pybase64-1.3.2/base64/test/codec_supported.h +0 -3
  72. pybase64-1.3.2/pyproject.toml +0 -19
  73. pybase64-1.3.2/src/pybase64/_version.py +0 -1
  74. {pybase64-1.3.2 → pybase64-1.4.1}/LICENSE +0 -0
  75. {pybase64-1.3.2 → pybase64-1.4.1}/base64/LICENSE +0 -0
  76. {pybase64-1.3.2 → pybase64-1.4.1}/base64/README.md +0 -0
  77. {pybase64-1.3.2 → pybase64-1.4.1}/base64/base64-benchmarks.png +0 -0
  78. {pybase64-1.3.2 → pybase64-1.4.1}/base64/cmake/Modules/TargetArch.cmake +0 -0
  79. {pybase64-1.3.2 → pybase64-1.4.1}/base64/cmake/Modules/TargetSIMDInstructionSet.cmake +0 -0
  80. {pybase64-1.3.2 → pybase64-1.4.1}/base64/cmake/base64-config.cmake.in +0 -0
  81. {pybase64-1.3.2 → pybase64-1.4.1}/base64/cmake/config.h.in +0 -0
  82. {pybase64-1.3.2 → pybase64-1.4.1}/base64/cmake/test-arch.c +0 -0
  83. {pybase64-1.3.2 → pybase64-1.4.1}/base64/include/libbase64.h +0 -0
  84. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/arch/avx/enc_loop_asm.c +0 -0
  85. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/arch/avx2/enc_loop_asm.c +0 -0
  86. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/arch/generic/dec_head.c +0 -0
  87. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/arch/generic/dec_tail.c +0 -0
  88. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/arch/generic/enc_head.c +0 -0
  89. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/arch/generic/enc_tail.c +0 -0
  90. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/arch/neon64/dec_loop.c +0 -0
  91. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/arch/neon64/enc_loop_asm.c +0 -0
  92. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/arch/ssse3/enc_loop_asm.c +0 -0
  93. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/exports.txt +0 -0
  94. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/lib.c +0 -0
  95. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/lib_openmp.c +0 -0
  96. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/tables/Makefile +0 -0
  97. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/tables/table_dec_32bit.h +0 -0
  98. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/tables/table_enc_12bit.h +0 -0
  99. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/tables/table_enc_12bit.py +0 -0
  100. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/tables/table_generator.c +0 -0
  101. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/tables/tables.c +0 -0
  102. {pybase64-1.3.2 → pybase64-1.4.1}/base64/lib/tables/tables.h +0 -0
  103. {pybase64-1.3.2 → pybase64-1.4.1}/base64/test/moby_dick.h +0 -0
  104. {pybase64-1.3.2 → pybase64-1.4.1}/base64/test/moby_dick_base64.txt +0 -0
  105. {pybase64-1.3.2 → pybase64-1.4.1}/base64/test/moby_dick_plain.txt +0 -0
  106. {pybase64-1.3.2 → pybase64-1.4.1}/src/pybase64/_license.py +0 -0
  107. {pybase64-1.3.2 → pybase64-1.4.1}/src/pybase64/_pybase64_get_simd_flags.c +0 -0
  108. {pybase64-1.3.2 → pybase64-1.4.1}/src/pybase64/_pybase64_get_simd_flags.h +0 -0
  109. {pybase64-1.3.2 → pybase64-1.4.1}/src/pybase64/py.typed +0 -0
  110. {pybase64-1.3.2 → pybase64-1.4.1}/src/pybase64.egg-info/dependency_links.txt +0 -0
  111. {pybase64-1.3.2 → pybase64-1.4.1}/src/pybase64.egg-info/entry_points.txt +0 -0
  112. {pybase64-1.3.2 → pybase64-1.4.1}/src/pybase64.egg-info/top_level.txt +0 -0
@@ -4,6 +4,9 @@ include LICENSE
4
4
  # Include headers
5
5
  include src/pybase64/_pybase64_get_simd_flags.h
6
6
 
7
+ # Include type stub for extension
8
+ include src/pybase64/_pybase64.pyi
9
+
7
10
  # Include full base64 folder
8
11
  graft base64
9
12
  # but the git folder
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: pybase64
3
- Version: 1.3.2
3
+ Version: 1.4.1
4
4
  Summary: Fast Base64 encoding/decoding
5
5
  Home-page: https://github.com/mayeut/pybase64
6
6
  Author: Matthieu Darbois
@@ -16,16 +16,25 @@ Classifier: Topic :: Utilities
16
16
  Classifier: License :: OSI Approved :: BSD License
17
17
  Classifier: Programming Language :: C
18
18
  Classifier: Programming Language :: Python :: 3
19
- Classifier: Programming Language :: Python :: 3.6
20
- Classifier: Programming Language :: Python :: 3.7
21
19
  Classifier: Programming Language :: Python :: 3.8
22
20
  Classifier: Programming Language :: Python :: 3.9
23
21
  Classifier: Programming Language :: Python :: 3.10
24
22
  Classifier: Programming Language :: Python :: 3.11
25
23
  Classifier: Programming Language :: Python :: 3.12
26
- Requires-Python: >=3.6
24
+ Classifier: Programming Language :: Python :: 3.13
25
+ Requires-Python: >=3.8
27
26
  Description-Content-Type: text/x-rst
28
27
  License-File: LICENSE
28
+ Dynamic: author
29
+ Dynamic: classifier
30
+ Dynamic: description
31
+ Dynamic: description-content-type
32
+ Dynamic: home-page
33
+ Dynamic: keywords
34
+ Dynamic: license
35
+ Dynamic: project-url
36
+ Dynamic: requires-python
37
+ Dynamic: summary
29
38
 
30
39
  .. SETUP VARIABLES
31
40
  .. |license-status| image:: https://img.shields.io/badge/license-BSD%202--Clause-blue.svg
@@ -150,6 +159,19 @@ Running Python 3.7.2, Apple LLVM version 10.0.0 (clang-1000.11.45.5), Mac OS X 1
150
159
 
151
160
  Changelog
152
161
  =========
162
+ 1.4.1
163
+ -----
164
+ - Publish PyPy 3.11 wheels
165
+ - Publish armv7l wheels
166
+
167
+ 1.4.0
168
+ -----
169
+ - Publish python 3.13 wheels
170
+ - Add support for free-threaded builds
171
+ - Add MSYS2 support for C-extension
172
+ - Better logging on base64 build failure when C-extension build is optional
173
+ - Drop python 3.6 & 3.7 support
174
+
153
175
  1.3.2
154
176
  -----
155
177
  - Update base64 library
@@ -121,6 +121,19 @@ Running Python 3.7.2, Apple LLVM version 10.0.0 (clang-1000.11.45.5), Mac OS X 1
121
121
 
122
122
  Changelog
123
123
  =========
124
+ 1.4.1
125
+ -----
126
+ - Publish PyPy 3.11 wheels
127
+ - Publish armv7l wheels
128
+
129
+ 1.4.0
130
+ -----
131
+ - Publish python 3.13 wheels
132
+ - Add support for free-threaded builds
133
+ - Add MSYS2 support for C-extension
134
+ - Better logging on base64 build failure when C-extension build is optional
135
+ - Drop python 3.6 & 3.7 support
136
+
124
137
  1.3.2
125
138
  -----
126
139
  - Update base64 library
@@ -5,10 +5,10 @@ on: [push, pull_request]
5
5
  jobs:
6
6
  makefile-analysis:
7
7
  name: makefile-analysis
8
- runs-on: ubuntu-22.04
8
+ runs-on: ubuntu-24.04
9
9
  steps:
10
10
  - name: Checkout
11
- uses: actions/checkout@v3
11
+ uses: actions/checkout@v4
12
12
  - name: "Install analysis tools"
13
13
  run: |
14
14
  sudo apt-get update
@@ -23,11 +23,11 @@ jobs:
23
23
  strategy:
24
24
  fail-fast: false
25
25
  matrix:
26
- runner: ["ubuntu-20.04"]
26
+ runner: ["ubuntu-22.04"]
27
27
  compiler: ["gcc", "clang"]
28
28
  openmp: ["0", "1"]
29
29
  include:
30
- - runner: "macos-11"
30
+ - runner: "macos-13"
31
31
  compiler: "clang"
32
32
  openmp: "0"
33
33
  env:
@@ -37,7 +37,12 @@ jobs:
37
37
  OBJCOPY: ${{ (startsWith(matrix.runner, 'macos') && 'echo') || 'objcopy' }}
38
38
  steps:
39
39
  - name: Checkout
40
- uses: actions/checkout@v3
40
+ uses: actions/checkout@v4
41
+ - name: Install Ubuntu libomp support
42
+ if: runner.os == 'Linux' && matrix.compiler == 'clang' && matrix.openmp == 1
43
+ run: |
44
+ sudo apt-get update
45
+ sudo apt-get install -y libomp-dev
41
46
  - name: Run tests
42
47
  run: ./test/ci/test.sh
43
48
 
@@ -48,14 +53,18 @@ jobs:
48
53
  strategy:
49
54
  fail-fast: false
50
55
  matrix:
51
- runner: ["ubuntu-20.04", "macos-11", "windows-2019"]
56
+ runner: ["ubuntu-22.04", "windows-2019"]
52
57
  platform: ["x86_64", "i686"]
53
- exclude:
54
- - runner: "macos-11"
55
- platform: "i686"
58
+ include:
59
+ - runner: "macos-13"
60
+ platform: "x86_64"
61
+ - runner: "macos-14"
62
+ platform: "arm64"
63
+ - runner: "ubuntu-22.04-arm"
64
+ platform: "aarch64"
56
65
  steps:
57
66
  - name: Checkout
58
- uses: actions/checkout@v3
67
+ uses: actions/checkout@v4
59
68
  - name: Install Ubuntu i686 support
60
69
  if: runner.os == 'Linux' && matrix.platform == 'i686'
61
70
  run: |
@@ -70,13 +79,13 @@ jobs:
70
79
  -Werror=dev
71
80
  -DBASE64_BUILD_TESTS=ON
72
81
  ${{ runner.os != 'Windows' && '-DCMAKE_BUILD_TYPE=Release' || '' }}
73
- ${{ runner.os == 'macOS' && '-DBASE64_WITH_AVX2=OFF' || '' }}
74
82
  ${{ runner.os == 'Windows' && matrix.platform == 'i686' && '-A Win32' || '' }}
75
- -DBASE64_WITH_AVX512=OFF
76
83
  - name: CMake Build
77
84
  run: cmake --build out --config Release --verbose
78
85
  - name: CTest
79
86
  run: ctest --no-tests=error --test-dir out -VV --build-config Release
87
+ env:
88
+ BASE64_TEST_SKIP_AVX512: "1"
80
89
 
81
90
  alpine-makefile-test:
82
91
  name: makefile-alpine-amd64-gcc
@@ -90,7 +99,7 @@ jobs:
90
99
  - name: Install deps
91
100
  run: apk add --update bash build-base git
92
101
  - name: Checkout
93
- uses: actions/checkout@v3
102
+ uses: actions/checkout@v4
94
103
  - name: Run tests
95
104
  run: ./test/ci/test.sh
96
105
 
@@ -104,20 +113,21 @@ jobs:
104
113
  - name: Install deps
105
114
  run: apk add --update bash build-base cmake git
106
115
  - name: Checkout
107
- uses: actions/checkout@v3
116
+ uses: actions/checkout@v4
108
117
  - name: CMake Configure
109
118
  run: >
110
119
  cmake
111
120
  -B out
112
121
  -Werror=dev
113
122
  -DBASE64_BUILD_TESTS=ON
114
- -DBASE64_WITH_AVX512=OFF
115
123
  -DCMAKE_BUILD_TYPE=Release
116
124
  - name: CMake Build
117
125
  run: cmake --build out --config Release --verbose
118
126
  - name: CTest
119
127
  run: ctest --no-tests=error -VV --build-config Release
120
128
  working-directory: ./out
129
+ env:
130
+ BASE64_TEST_SKIP_AVX512: "1"
121
131
 
122
132
  alpine-alt-arch-makefile-test:
123
133
  name: makefile-alpine-${{matrix.arch}}-${{matrix.cc}}
@@ -130,7 +140,7 @@ jobs:
130
140
  cc: [gcc, clang]
131
141
  steps:
132
142
  - name: Checkout
133
- uses: actions/checkout@v3
143
+ uses: actions/checkout@v4
134
144
  - uses: uraimo/run-on-arch-action@v2
135
145
  with:
136
146
  arch: ${{matrix.arch}}
@@ -151,7 +161,7 @@ jobs:
151
161
  cc: [gcc, clang]
152
162
  steps:
153
163
  - name: Checkout
154
- uses: actions/checkout@v3
164
+ uses: actions/checkout@v4
155
165
  - uses: uraimo/run-on-arch-action@v2
156
166
  with:
157
167
  arch: ${{matrix.arch}}
@@ -169,3 +179,84 @@ jobs:
169
179
  echo "::group::CTest"
170
180
  ctest --no-tests=error --test-dir out -VV --build-config Release
171
181
  echo "::endgroup::CTest"
182
+
183
+ msys2-cmake-test:
184
+ name: msys2-cmake-${{ matrix.msystem }}
185
+ needs: makefile-analysis
186
+ runs-on: windows-2022
187
+ strategy:
188
+ fail-fast: false
189
+ matrix:
190
+ include:
191
+ - { msystem: msys, toolchain: "gcc" }
192
+ - { msystem: mingw32, env: mingw-w64-i686- }
193
+ - { msystem: mingw64, env: mingw-w64-x86_64- }
194
+ - { msystem: ucrt64, env: mingw-w64-ucrt-x86_64- }
195
+ - { msystem: clang64, env: mingw-w64-clang-x86_64- }
196
+
197
+ steps:
198
+ - name: Checkout
199
+ uses: actions/checkout@v4
200
+ - name: Setup MSYS2 ${{matrix.msystem}}
201
+ uses: msys2/setup-msys2@v2
202
+ with:
203
+ msystem: ${{matrix.msystem}}
204
+ update: true
205
+ install: >-
206
+ make
207
+ ${{ matrix.env }}${{ matrix.toolchain || 'toolchain' }}
208
+ ${{ matrix.env }}cmake
209
+ - name: CMake Configure
210
+ shell: msys2 {0}
211
+ run: >
212
+ cmake
213
+ -B out
214
+ -Werror=dev
215
+ -DBASE64_BUILD_TESTS=ON
216
+ -DCMAKE_BUILD_TYPE=Release
217
+ - name: CMake Build
218
+ shell: msys2 {0}
219
+ run: cmake --build out --config Release --verbose
220
+ - name: CTest
221
+ shell: msys2 {0}
222
+ run: ctest --no-tests=error --test-dir out -VV --build-config Release
223
+ env:
224
+ BASE64_TEST_SKIP_AVX512: "1"
225
+ - name: Test demo utility with unicode filenames and file contents on Windows
226
+ shell: msys2 {0}
227
+ run: |
228
+ out/bin/base64 test/ci/😵‍💫.bin > test/ci/😵‍💫.bin.b64
229
+ out/bin/base64 -d test/ci/😵‍💫.bin.b64 > test/ci/😵‍💫.bin
230
+ cd test/ci
231
+ sha256sum -c checksums.txt
232
+
233
+ msys2-makefile-test:
234
+ name: msys2-makefile-${{ matrix.msystem }}
235
+ needs: makefile-analysis
236
+ runs-on: windows-2022
237
+ strategy:
238
+ fail-fast: false
239
+ matrix:
240
+ include:
241
+ - { msystem: msys, toolchain: "gcc" }
242
+ - { msystem: mingw32, env: mingw-w64-i686- }
243
+ - { msystem: mingw64, env: mingw-w64-x86_64- }
244
+ - { msystem: ucrt64, env: mingw-w64-ucrt-x86_64- }
245
+ # - { msystem: clang32, env: mingw-w64-clang-i686- } disabled, lld does not support the "-r" option
246
+ # - { msystem: clang64, env: mingw-w64-clang-x86_64- } disabled, lld does not support the "-r" option
247
+ env:
248
+ CC: cc.exe
249
+ steps:
250
+ - name: Checkout
251
+ uses: actions/checkout@v4
252
+ - name: Setup MSYS2 ${{matrix.msystem}}
253
+ uses: msys2/setup-msys2@v2
254
+ with:
255
+ msystem: ${{matrix.msystem}}
256
+ update: true
257
+ install: >-
258
+ make
259
+ ${{ matrix.env }}${{ matrix.toolchain || 'toolchain' }}
260
+ - name: Run tests
261
+ shell: msys2 {0}
262
+ run: ./test/ci/test.sh
@@ -17,7 +17,7 @@ if (POLICY CMP0127)
17
17
  cmake_policy(SET CMP0127 NEW)
18
18
  endif()
19
19
 
20
- project(base64 LANGUAGES C VERSION 0.5.1)
20
+ project(base64 LANGUAGES C VERSION 0.5.2)
21
21
 
22
22
  include(GNUInstallDirs)
23
23
  include(CMakeDependentOption)
@@ -249,6 +249,10 @@ if (BASE64_BUILD_CLI)
249
249
  set_target_properties(base64-bin PROPERTIES
250
250
  OUTPUT_NAME base64
251
251
  )
252
+
253
+ if (WIN32)
254
+ target_sources(base64-bin PRIVATE bin/base64.rc)
255
+ endif ()
252
256
  endif()
253
257
 
254
258
  ########################################################################
@@ -1,4 +1,4 @@
1
- CFLAGS += -std=c99 -O3 -Wall -Wextra -pedantic
1
+ CFLAGS += -std=c99 -O3 -Wall -Wextra -pedantic -DBASE64_STATIC_DEFINE
2
2
 
3
3
  # Set OBJCOPY if not defined by environment:
4
4
  OBJCOPY ?= objcopy
@@ -56,6 +56,7 @@ ifdef OPENMP
56
56
  CFLAGS += -fopenmp
57
57
  endif
58
58
 
59
+ TARGET := $(shell $(CC) -dumpmachine)
59
60
 
60
61
  .PHONY: all analyze clean
61
62
 
@@ -64,9 +65,17 @@ all: bin/base64 lib/libbase64.o
64
65
  bin/base64: bin/base64.o lib/libbase64.o
65
66
  $(CC) $(CFLAGS) -o $@ $^
66
67
 
67
- lib/libbase64.o: $(OBJS)
68
- $(LD) -r -o $@ $^
69
- $(OBJCOPY) --keep-global-symbols=lib/exports.txt $@
68
+ # Workaround: mangle exported function names on MinGW32.
69
+ lib/exports.build.txt: lib/exports.txt
70
+ ifeq (i686-w64-mingw32, $(TARGET))
71
+ sed -e 's/^/_/' $< > $@
72
+ else
73
+ cp -f $< $@
74
+ endif
75
+
76
+ lib/libbase64.o: lib/exports.build.txt $(OBJS)
77
+ $(LD) -r -o $@ $(OBJS)
78
+ $(OBJCOPY) --keep-global-symbols=$< $@
70
79
 
71
80
  lib/config.h:
72
81
  @echo "#define HAVE_AVX512 $(HAVE_AVX512)" > $@
@@ -97,4 +106,4 @@ analyze: clean
97
106
  scan-build --use-analyzer=`which clang` --status-bugs make
98
107
 
99
108
  clean:
100
- rm -f bin/base64 bin/base64.o lib/libbase64.o lib/config.h $(OBJS)
109
+ rm -f bin/base64 bin/base64.o lib/libbase64.o lib/config.h lib/exports.build.txt $(OBJS)
@@ -1,4 +1,24 @@
1
- #define _XOPEN_SOURCE // IOV_MAX
1
+ // Test for MinGW.
2
+ #if defined(__MINGW32__) || defined(__MINGW64__)
3
+ # define MINGW
4
+ #endif
5
+
6
+ // Test for Windows.
7
+ #if defined(_WIN32) || defined(_WIN64)
8
+ # define WIN
9
+ #endif
10
+
11
+ // Decide if the writev(2) system call needs to be emulated as a series of
12
+ // write(2) calls. At least MinGW does not support writev(2).
13
+ #ifdef MINGW
14
+ # define EMULATE_WRITEV
15
+ #endif
16
+
17
+ // Include the necessary system header when using the system's writev(2).
18
+ #ifndef EMULATE_WRITEV
19
+ # define _XOPEN_SOURCE // Unlock IOV_MAX
20
+ # include <sys/uio.h>
21
+ #endif
2
22
 
3
23
  #include <stdbool.h>
4
24
  #include <stdlib.h>
@@ -8,7 +28,13 @@
8
28
  #include <getopt.h>
9
29
  #include <errno.h>
10
30
  #include <limits.h>
11
- #include <sys/uio.h>
31
+
32
+ // Include Windows-specific headers.
33
+ #ifdef WIN
34
+ # include <io.h>
35
+ # include <fcntl.h>
36
+ #endif
37
+
12
38
  #include "../include/libbase64.h"
13
39
 
14
40
  // Size of the buffer for the "raw" (not base64-encoded) data in bytes.
@@ -38,6 +64,12 @@ struct config {
38
64
 
39
65
  // Whether to just print the help text and exit.
40
66
  bool print_help;
67
+
68
+ // Whether to strip newlines from the input when decoding.
69
+ bool strip_newlines;
70
+
71
+ // Whether to ignore any character not in the base64 alphabet.
72
+ bool ignore_garbage;
41
73
  };
42
74
 
43
75
  // Input/output buffer structure.
@@ -50,6 +82,59 @@ struct buffer {
50
82
  char *enc;
51
83
  };
52
84
 
85
+ // Optionally emulate writev(2) as a series of write calls.
86
+ #ifdef EMULATE_WRITEV
87
+
88
+ // Quick and dirty definition of IOV_MAX as it is probably not defined.
89
+ #ifndef IOV_MAX
90
+ # define IOV_MAX 1024
91
+ #endif
92
+
93
+ // Quick and dirty definition of this system struct, for local use only.
94
+ struct iovec {
95
+
96
+ // Opaque data pointer.
97
+ void *iov_base;
98
+
99
+ // Length of the data in bytes.
100
+ size_t iov_len;
101
+ };
102
+
103
+ static ssize_t
104
+ writev (const int fd, const struct iovec *iov, int iovcnt)
105
+ {
106
+ ssize_t r, nwrite = 0;
107
+
108
+ // Reset the error marker.
109
+ errno = 0;
110
+
111
+ while (iovcnt-- > 0) {
112
+
113
+ // Write the vector; propagate errors back to the caller. Note
114
+ // that this loses information about how much vectors have been
115
+ // successfully written, but that also seems to be the case
116
+ // with the real function. The API is somewhat flawed.
117
+ if ((r = write(fd, iov->iov_base, iov->iov_len)) < 0) {
118
+ return r;
119
+ }
120
+
121
+ // Update the total write count.
122
+ nwrite += r;
123
+
124
+ // Return early after a partial write; the caller should retry.
125
+ if ((size_t) r != iov->iov_len) {
126
+ break;
127
+ }
128
+
129
+ // Move to the next vector.
130
+ iov++;
131
+ }
132
+
133
+ return nwrite;
134
+ }
135
+
136
+ #endif // EMULATE_WRITEV
137
+
53
138
  static bool
54
139
  buffer_alloc (const struct config *config, struct buffer *buf)
55
140
  {
@@ -272,10 +357,54 @@ encode (const struct config *config, struct buffer *buf)
272
357
  return true;
273
358
  }
274
359
 
275
- static int
360
+ static inline size_t
361
+ find_garbage (const char *p, const size_t avail)
362
+ {
363
+ // Use a lookup table to distinguish garbage from non-garbage.
364
+ static const char lut[256] = {
365
+ ['A'] = 1, ['B'] = 1, ['C'] = 1, ['D'] = 1, ['E'] = 1,
366
+ ['F'] = 1, ['G'] = 1, ['H'] = 1, ['I'] = 1, ['J'] = 1,
367
+ ['K'] = 1, ['L'] = 1, ['M'] = 1, ['N'] = 1, ['O'] = 1,
368
+ ['P'] = 1, ['Q'] = 1, ['R'] = 1, ['S'] = 1, ['T'] = 1,
369
+ ['U'] = 1, ['V'] = 1, ['W'] = 1, ['X'] = 1, ['Y'] = 1,
370
+ ['Z'] = 1,
371
+ ['a'] = 1, ['b'] = 1, ['c'] = 1, ['d'] = 1, ['e'] = 1,
372
+ ['f'] = 1, ['g'] = 1, ['h'] = 1, ['i'] = 1, ['j'] = 1,
373
+ ['k'] = 1, ['l'] = 1, ['m'] = 1, ['n'] = 1, ['o'] = 1,
374
+ ['p'] = 1, ['q'] = 1, ['r'] = 1, ['s'] = 1, ['t'] = 1,
375
+ ['u'] = 1, ['v'] = 1, ['w'] = 1, ['x'] = 1, ['y'] = 1,
376
+ ['z'] = 1,
377
+ ['0'] = 1, ['1'] = 1, ['2'] = 1, ['3'] = 1, ['4'] = 1,
378
+ ['5'] = 1, ['6'] = 1, ['7'] = 1, ['8'] = 1, ['9'] = 1,
379
+ ['+'] = 1, ['/'] = 1, ['"'] = 1,
380
+ };
381
+
382
+ for (size_t len = 0; len < avail; len++) {
383
+ if (lut[(unsigned char) p[len]] == 0) {
384
+ return len;
385
+ }
386
+ }
387
+
388
+ return avail;
389
+ }
390
+
391
+ static inline size_t
392
+ find_newline (const char *p, const size_t avail)
393
+ {
394
+ // This is very naive and can probably be improved by vectorization.
395
+ for (size_t len = 0; len < avail; len++) {
396
+ if (p[len] == '\n') {
397
+ return len;
398
+ }
399
+ }
400
+
401
+ return avail;
402
+ }
403
+
404
+ static bool
276
405
  decode (const struct config *config, struct buffer *buf)
277
406
  {
278
- size_t nread, nout;
407
+ size_t avail;
279
408
  struct base64_state state;
280
409
 
281
410
  // Initialize the decoder's state structure.
@@ -283,18 +412,61 @@ decode (const struct config *config, struct buffer *buf)
283
412
 
284
413
  // Read encoded data into the buffer. Use the smallest buffer size to
285
414
  // be on the safe side: the decoded output will fit the raw buffer.
286
- while ((nread = fread(buf->enc, 1, BUFFER_RAW_SIZE, config->fp)) > 0) {
415
+ while ((avail = fread(buf->enc, 1, BUFFER_RAW_SIZE, config->fp)) > 0) {
416
+ char *start = buf->enc;
417
+ char *outbuf = buf->raw;
418
+ size_t ototal = 0;
419
+
420
+ // By popular demand, this utility tries to be bug-compatible
421
+ // with GNU `base64'. That includes silently ignoring newlines
422
+ // in the input. Tokenize the input on newline characters.
423
+ while (avail > 0) {
424
+ size_t outlen, len;
425
+
426
+ // When stripping garbage or newlines in the input,
427
+ // find the offset of the next garbage/newline
428
+ // character, which is also the length of the next
429
+ // chunk. Otherwise treat the entire input as a single
430
+ // chunk.
431
+ if (config->ignore_garbage) {
432
+ len = find_garbage(start, avail);
433
+ } else if (config->strip_newlines) {
434
+ len = find_newline(start, avail);
435
+ } else {
436
+ len = avail;
437
+ }
287
438
 
288
- // Decode the input into the raw buffer.
289
- if (base64_stream_decode(&state, buf->enc, nread,
290
- buf->raw, &nout) == 0) {
291
- fprintf(stderr, "%s: %s: decoding error\n",
292
- config->name, config->file);
293
- return false;
439
+ // Ignore empty chunks.
440
+ if (len == 0) {
441
+ start++;
442
+ avail--;
443
+ continue;
444
+ }
445
+
446
+ // Decode the chunk into the raw buffer.
447
+ if (base64_stream_decode(&state, start, len,
448
+ outbuf, &outlen) == 0) {
449
+ fprintf(stderr, "%s: %s: decoding error\n",
450
+ config->name, config->file);
451
+ return false;
452
+ }
453
+
454
+ // Update the output buffer pointer and total size.
455
+ outbuf += outlen;
456
+ ototal += outlen;
457
+
458
+ // Bail out if the whole string has been consumed.
459
+ if (len == avail) {
460
+ break;
461
+ }
462
+
463
+ // Move the start pointer past the newline.
464
+ start += len + 1;
465
+ avail -= len + 1;
294
466
  }
295
467
 
296
468
  // Append the raw data to the output stream.
297
- if (write_stdout(config, buf->raw, nout) == false) {
469
+ if (write_stdout(config, buf->raw, ototal) == false) {
298
470
  return false;
299
471
  }
300
472
  }
@@ -317,10 +489,17 @@ usage (FILE *fp, const struct config *config)
317
489
  "If no FILE is given or is specified as '-', "
318
490
  "read from standard input.\n"
319
491
  "Options:\n"
320
- " -d, --decode Decode a base64 stream.\n"
321
- " -h, --help Print this help text.\n"
322
- " -w, --wrap=COLS Wrap encoded lines at this column. "
323
- "Default 76, 0 to disable.\n";
492
+ " -d, --decode Decode a base64 stream.\n"
493
+ " -h, --help Print this help text.\n"
494
+ " -i, --ignore-garbage When decoding, ignore any "
495
+ "non-base64 data.\n"
496
+ " -n, --no-strip-newlines When decoding, do not strip "
497
+ "newlines. Speeds up\n"
498
+ " decoding of inputs that do not "
499
+ "contain newlines.\n"
500
+ " -w, --wrap=COLS Wrap encoded lines at this "
501
+ "column. Default 76, 0 to\n"
502
+ " disable.\n";
324
503
 
325
504
  fprintf(fp, usage, config->name);
326
505
  }
@@ -357,9 +536,11 @@ parse_opts (int argc, char **argv, struct config *config)
357
536
  {
358
537
  int c;
359
538
  static const struct option opts[] = {
360
- { "decode", no_argument, NULL, 'd' },
361
- { "help", no_argument, NULL, 'h' },
362
- { "wrap", required_argument, NULL, 'w' },
539
+ { "decode", no_argument, NULL, 'd' },
540
+ { "help", no_argument, NULL, 'h' },
541
+ { "ignore-garbage", no_argument, NULL, 'i' },
542
+ { "no-strip-newlines", no_argument, NULL, 'n' },
543
+ { "wrap", required_argument, NULL, 'w' },
363
544
  { NULL }
364
545
  };
365
546
 
@@ -367,7 +548,7 @@ parse_opts (int argc, char **argv, struct config *config)
367
548
  config->name = *argv;
368
549
 
369
550
  // Parse command line options.
370
- while ((c = getopt_long(argc, argv, ":dhw:", opts, NULL)) != -1) {
551
+ while ((c = getopt_long(argc, argv, ":dhinw:", opts, NULL)) != -1) {
371
552
  switch (c) {
372
553
  case 'd':
373
554
  config->decode = true;
@@ -377,6 +558,14 @@ parse_opts (int argc, char **argv, struct config *config)
377
558
  config->print_help = true;
378
559
  return true;
379
560
 
561
+ case 'i':
562
+ config->ignore_garbage = true;
563
+ break;
564
+
565
+ case 'n':
566
+ config->strip_newlines = false;
567
+ break;
568
+
380
569
  case 'w':
381
570
  if (get_wrap(config, optarg) == false) {
382
571
  fprintf(stderr,
@@ -433,11 +622,13 @@ main (int argc, char **argv)
433
622
  {
434
623
  // Default program config.
435
624
  struct config config = {
436
- .file = "stdin",
437
- .fp = stdin,
438
- .wrap = 76,
439
- .decode = false,
440
- .print_help = false,
625
+ .file = "stdin",
626
+ .fp = stdin,
627
+ .wrap = 76,
628
+ .decode = false,
629
+ .print_help = false,
630
+ .strip_newlines = true,
631
+ .ignore_garbage = false,
441
632
  };
442
633
  struct buffer buf;
443
634
 
@@ -458,6 +649,15 @@ main (int argc, char **argv)
458
649
  return 1;
459
650
  }
460
651
 
652
+ #ifdef WIN
653
+
654
+ // On Windows, ensure that stdout is binary-clean, and does not
655
+ // silently convert newlines to CRLFs. This seems to be the portable
656
+ // way to do it; freopen() and SetConsoleMode() occasionally result in
657
+ // permission errors, whereas this always seems to work.
658
+ _setmode(1, _O_BINARY);
659
+ #endif
660
+
461
661
  // Encode or decode the input based on the user's choice.
462
662
  const bool ret = config.decode
463
663
  ? decode(&config, &buf)