oj_windows 3.16.15 → 3.17.2.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 608dd07735fb09d340eac0f8adc2d9bce84cf1b0bfea863f74ef9c5ff26dd322
4
- data.tar.gz: 11920df4ad7ba7696f979c35aa54ce383d5855b99c07e9449aa7a1c6f62ba6d3
3
+ metadata.gz: ed00cf974105c81c2e38ca66a5a00bcf6d6e32106764537fdbc58264cf96912f
4
+ data.tar.gz: c9440ab13621b80ffa95fef72a56c02dc362c1282172c6676d5445b53a6a1648
5
5
  SHA512:
6
- metadata.gz: 1975faac825fcf5dd9f851829dcbea96e4f0487a678161841f630a3a97a4f3acceadedfbbbc379ac01642c0c2d08c3a60f8a7f405f4f5c5fb1a02c097cf12d64
7
- data.tar.gz: 249dbbe5e901655c192c94453aac166f45af7da6d84effbaf40935ce262d6346ffbe81cf78085f0491fafa35cd01ca0131bb0184f658f416faaebf3b8b61a4f1
6
+ metadata.gz: bb72bfd23933e0e0dbe2e2e58f9a6eb6855fca6f5832f3e1b5b7abb7f716dd618f6dbf90150d539677cbb03f6ba76f523b25a60aabc6f02a691cdc9a2e05e821
7
+ data.tar.gz: 7e7bfc7b9376a3ab6fb164ce8c0d85d10faed4ab9dd63d5ee455311aa3f293f8f3640b4dd6146d2c32368fd29f7b924dcd788b06616832de7c0333ebb51f71ee
data/CHANGELOG.md CHANGED
@@ -2,6 +2,79 @@
2
2
 
3
3
  All notable changes to `oj_windows` will be documented in this file.
4
4
 
5
+ ## [3.17.2.1] - 2026-05-29
6
+
7
+ Fork revision based on upstream Oj 3.17.2. The version is `3.17.2.1` (not plain
8
+ `3.17.2`) because this carries upstream's parser feature set through 3.17.2 plus
9
+ the MSVC/SIMD adaptations below; it is not a byte-identical rebase of 3.17.2.
10
+
11
+ ### Performance
12
+ - Enabled SIMD string scanning under MSVC. `simd.h` previously gated SSE on the
13
+ GCC/Clang `__SSE2__`/`__SSE4_2__` feature macros, which `cl.exe` never defines,
14
+ so the scalar scanner was always used on the only supported platform. SSE2 is
15
+ now enabled by default on x86_64 (~20–25% faster parsing on string-heavy
16
+ payloads). Added MSVC shims for the GCC builtins used by the SSE scanners
17
+ (`__builtin_prefetch`/`__builtin_expect`/`__builtin_ctz`).
18
+ - The SSE4.2 (PCMPESTRI) scanner is also compiled and selectable, but SSE2 is the
19
+ default because PCMPESTRI is slower on common microarchitectures.
20
+ - Added a `--disable-simd` build option and an `OJ_SCAN=scalar|sse2|sse42` runtime
21
+ override (replacing the previously non-functional `--with-sse42` flag).
22
+
23
+ ### Security / robustness (ported from upstream Oj 3.16.16–3.17.2)
24
+ - `Oj::Parser` (the "usual" parser) now validates that the document is complete:
25
+ incomplete literals (`tru`, `nul`, `n`, …) and unclosed arrays/objects (`[1,2`,
26
+ `{"a":1`) are rejected instead of being silently accepted. Ported upstream's
27
+ `validate_document_end` (raises `EncodingError`/`Oj::ParserError` with
28
+ "expected …" / "… is not closed"). `test_parser_usual.rb#test_nil` was updated
29
+ to match the stricter, upstream behavior.
30
+ - Added the **`Oj::Parser.safe`** parser (upstream 3.17.0): builds Ruby objects
31
+ like `:usual` but enforces configurable limits on untrusted input —
32
+ `:max_hash_size`, `:max_array_size`, `:max_depth`, `:max_total_elements` —
33
+ raising `Oj::Parser::ValidationError` subclasses (`HashSizeError`,
34
+ `ArraySizeError`, `DepthError`, `TotalElementsError`). Ported `safe.c`/`safe.h`.
35
+ - Added the upstream 3.17.2 "extreme sizes" key-length guard: object keys longer
36
+ than 32,000 bytes now raise instead of overflowing the `int16_t` key length.
37
+ - Fixed an uninitialized-pointer dereference in the streaming parser: `sparse.c`
38
+ never set `ni.pi`, but `oj_num_as_value` dereferences `ni->pi->err_class` on the
39
+ invalid-float path. Initialized `ni.pi` at all three number sites (matches
40
+ upstream).
41
+ - Note: the non-Windows file-read partial-read fix (#1004) is not reachable on
42
+ MSVC (that path is `#if !IS_WINDOWS`); the Windows-reachable `Oj::Parser#file`
43
+ read was fixed separately (see below).
44
+
45
+ ### Fixed
46
+ - `mem.c` (the `MEM_DEBUG` allocator) no longer fails to compile on MSVC: it now
47
+ uses a Windows `SRWLOCK` (which has a static initializer) instead of an
48
+ unguarded `pthread_mutex_t`.
49
+ - `Oj::Parser#file` captured the `read()` result in an unsigned `size_t`; on a
50
+ read error (`-1`) this wrapped to `SIZE_MAX`, causing an out-of-bounds write
51
+ and making the error branch unreachable. It now uses a signed count, handles
52
+ errors/EOF correctly, and closes the file descriptor.
53
+ - Guarded the remaining unconditional `<sys/types.h>` includes (`reader.c`,
54
+ `rxclass.c`) and added a guarded `ssize_t` fallback for MSVC.
55
+
56
+ ### Changed
57
+ - Removed the dead, byte-identical `lib/oj/` duplicate tree from the gem package;
58
+ `lib/oj_windows/` is the only tree that is ever loaded.
59
+ - gemspec now credits Peter Ohler and states the relationship to upstream Oj
60
+ (oj_windows replaces, and cannot be co-installed with, the `oj` gem).
61
+ - Build scripts (`package_install.bat`, `compile.bat`, `build_install.bat`) locate
62
+ Visual Studio via `vswhere` (and put the Installer dir on PATH so vcvars runs
63
+ cleanly) and derive the gem filename from `Oj::VERSION` instead of hardcoding a
64
+ VS path or version number.
65
+ - Added a GitHub Actions CI workflow (`.github/workflows/ci.yml`) using the mswin
66
+ (MSVC) Ruby: it compiles the extension, runs the core test suite, and verifies a
67
+ clean `gem install` against the Ruby headers alone.
68
+ - `extconf.rb` no longer runs `nmake clean`/`make clean` after `create_makefile`
69
+ (it ran during `gem install`, just before the build, so it was pointless noise).
70
+ - README: honest performance framing, a CI badge in place of the static
71
+ "373 Passing"/"High Performance" badges, and corrected test/skip counts (5 skips).
72
+
73
+ ## [3.16.15] - 2025-12-20
74
+
75
+ - Published release following 3.16.14. This entry was added retroactively to
76
+ document the version that was actually shipped to RubyGems.
77
+
5
78
  ## [3.16.14] - 2025-12-20
6
79
 
7
80
  ### Initial Release
data/README.md CHANGED
@@ -1,164 +1,168 @@
1
- # oj_windows
2
-
3
- **A Windows-native, high-performance JSON parser and Object marshaller for Ruby.**
4
-
5
- [![Gem Version](https://img.shields.io/gem/v/oj_windows.svg)](https://rubygems.org/gems/oj_windows)
6
-
7
- ## Overview
8
-
9
- `oj_windows` is a production-ready, Windows-exclusive fork optimized for the **Ruby 3.4.8 MSVC** environment. All POSIX-specific code has been removed or replaced with native Windows equivalents, ensuring maximum performance and stability on Windows systems.
10
-
11
- ## Requirements
12
-
13
- | Component | Version |
14
- |-----------|---------|
15
- | Ruby | 3.4.8+ (MSVC build) |
16
- | OS | Windows 10/11 x64 |
17
- | Compiler | Visual Studio 2022+ |
18
- | Architecture | x64-mswin64_140 |
19
-
20
- ## Installation
21
-
22
- ```powershell
23
- gem install oj_windows
24
- ```
25
-
26
- Or in your Gemfile:
27
-
28
- ```ruby
29
- gem 'oj_windows'
30
- ```
31
-
32
- ## Quick Start
33
-
34
- ```ruby
35
- require 'oj_windows'
36
-
37
- # Dump Ruby objects to JSON
38
- hash = { 'name' => 'oj_windows', 'version' => '3.16.14', 'windows' => true }
39
- json = Oj.dump(hash)
40
- # => {"name":"oj_windows","version":"3.16.14","windows":true}
41
-
42
- # Load JSON back to Ruby objects
43
- data = Oj.load(json)
44
- # => {"name"=>"oj_windows", "version"=>"3.16.14", "windows"=>true}
45
-
46
- # Pretty print
47
- puts Oj.dump(hash, indent: 2)
48
- ```
49
-
50
- ## Features
51
-
52
- - **High Performance**: Optimized C extension compiled with MSVC
53
- - **Multiple Modes**: Strict, Compat, Object, Custom, Rails, WAB, Null
54
- - **JSON Gem Compatibility**: Drop-in replacement via `Oj.mimic_JSON`
55
- - **Rails Integration**: Full ActiveSupport/ActiveRecord support
56
- - **Streaming**: SAJ and SC parsers for large documents
57
- - **StringWriter/StreamWriter**: Efficient JSON generation
58
-
59
- ## Test Results
60
-
61
- All tests pass on Windows MSVC Ruby 3.4.8:
62
-
63
- | Test Suite | Runs | Assertions | Failures | Errors | Skips |
64
- |------------|------|------------|----------|--------|-------|
65
- | test_various.rb | 69 | 8,202 | 0 | 0 | 1 |
66
- | test_strict.rb | 42 | 71 | 0 | 0 | 0 |
67
- | test_compat.rb | 55 | 112 | 0 | 0 | 0 |
68
- | test_object.rb | 74 | 160 | 0 | 0 | 0 |
69
- | test_fast.rb | 43 | 137 | 0 | 0 | 0 |
70
- | test_saj.rb | 13 | 18 | 0 | 0 | 0 |
71
- | test_scp.rb | 23 | 23 | 0 | 0 | 2 |
72
- | test_gc.rb | 3 | 50 | 0 | 0 | 0 |
73
- | test_writer.rb | 27 | 28 | 0 | 0 | 1 |
74
- | test_file.rb | 21 | 52 | 0 | 0 | 1 |
75
- | test_hash.rb | 3 | 7 | 0 | 0 | 0 |
76
- | **Total** | **373** | **8,860** | **0** | **0** | **6** |
77
-
78
- > **Note**: The 6 skipped tests are fork-based operations that are not supported on Windows. This is expected behavior.
79
-
80
- ## Performance Benchmarks
81
-
82
- Benchmarks run on Windows with Ruby 3.4.8 MSVC (50,000 iterations):
83
-
84
- ### Parse Performance
85
- | Parser | Ops/sec |
86
- |--------|---------|
87
- | JSON::Ext | 444,501 |
88
- | Oj:wab | 402,185 |
89
- | Oj:strict | 380,664 |
90
-
91
- ### Dump Performance
92
- | Dumper | Ops/sec |
93
- |--------|---------|
94
- | JSON::Ext | 832,994 |
95
- | Oj:strict | 816,299 |
96
-
97
- > **\*** In synthetic benchmarks with simple data structures, Ruby's native `JSON::Ext` may appear faster because it is tightly integrated with the Ruby VM. However, `oj_windows` provides significant advantages:
98
- > - **Object mode**: Full Ruby object serialization/deserialization (not possible with JSON gem)
99
- > - **Rails/ActiveSupport**: Optimized `as_json`/`to_json` integration
100
- > - **Streaming parsers**: SAJ and SCP for memory-efficient parsing of large documents
101
- > - **Circular references**: Automatic detection and handling
102
- > - **Custom behaviors**: Fine-grained control over serialization
103
- >
104
- > For complex real-world applications with deep object graphs, custom types, and Rails integration, `oj_windows` remains the optimal choice.
105
-
106
- ## Modes
107
-
108
- | Mode | Description |
109
- |------|-------------|
110
- | `:strict` | Strict JSON compliance, only native types |
111
- | `:compat` | Compatible with the JSON gem |
112
- | `:object` | Full Ruby object serialization |
113
- | `:custom` | Customizable serialization behavior |
114
- | `:rails` | Rails and ActiveSupport compatibility |
115
- | `:wab` | WAB (Web Application Builder) format |
116
- | `:null` | Return null for unsupported types |
117
-
118
- ## Configuration
119
-
120
- ```ruby
121
- # Set default options
122
- Oj.default_options = {
123
- mode: :compat,
124
- symbol_keys: true,
125
- indent: 2
126
- }
127
-
128
- # Check current options
129
- Oj.default_options
130
- ```
131
-
132
- ## Documentation
133
-
134
- - [Options](pages/Options.md) - Parse and dump options
135
- - [Modes](pages/Modes.md) - Detailed mode documentation
136
- - [Rails](pages/Rails.md) - Rails integration guide
137
- - [JsonGem](pages/JsonGem.md) - JSON gem compatibility
138
- - [Advanced](pages/Advanced.md) - Advanced features
139
-
140
- ## Building from Source
141
-
142
- ```powershell
143
- # Requires MSVC environment
144
- call "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat"
145
-
146
- # Build and install
147
- .\package_install.bat
148
- ```
149
-
150
- ## Running Tests
151
-
152
- ```powershell
153
- cd test
154
- .\test_all_no_rails.bat
155
- ```
156
-
157
- ## License
158
-
159
- MIT License - See [LICENSE](LICENSE) for details.
160
-
161
- ---
162
-
163
- *Version 3.16.14 - December 2025*
164
- *Original OJ https://github.com/ohler55/oj*
1
+ # oj_windows
2
+
3
+ **A Windows-native, high-performance JSON parser and Object marshaller for Ruby.**
4
+
5
+ [![Gem Version](https://img.shields.io/gem/v/oj_windows.svg?style=flat-square)](https://rubygems.org/gems/oj_windows)
6
+ [![Ruby](https://img.shields.io/badge/Ruby-3.4%2B-CC342D?style=flat-square&logo=ruby&logoColor=white)](https://www.ruby-lang.org/)
7
+ [![Platform](https://img.shields.io/badge/Platform-Windows%20MSVC-0078D6?style=flat-square&logo=windows&logoColor=white)](https://github.com/tigel-agm/oj_windows)
8
+ [![License](https://img.shields.io/badge/License-MIT-green?style=flat-square)](https://github.com/tigel-agm/oj_windows/blob/main/LICENSE)
9
+ [![CI](https://github.com/tigel-agm/oj_windows/actions/workflows/ci.yml/badge.svg)](https://github.com/tigel-agm/oj_windows/actions/workflows/ci.yml)
10
+
11
+ ## Overview
12
+
13
+ `oj_windows` is a fork of [Oj](https://github.com/ohler55/oj) by Peter Ohler, adapted to build with the **MSVC (mswin)** Ruby toolchain. POSIX-specific code has been guarded or replaced with native Windows equivalents (pthread mutexes → Windows synchronization, `timegm` → `_mkgmtime`, guarded POSIX headers), and SIMD string scanning is enabled under MSVC.
14
+
15
+ > **Relationship to upstream Oj.** Almost all of this code is Peter Ohler's. `oj_windows` exists only to provide a buildable Oj for Ruby compiled with the MSVC toolchain. It defines the same `Oj` module and the same public API, so **it is a drop-in replacement for — and cannot be installed alongside — the upstream `oj` gem**. If you use the standard **RubyInstaller / MinGW** Ruby, install the upstream [`oj`](https://rubygems.org/gems/oj) gem instead (it builds there via the MSYS2 devkit); `oj_windows` is only needed for native MSVC (`mswin`) builds.
16
+
17
+ ## Requirements
18
+
19
+ | Component | Version |
20
+ |-----------|---------|
21
+ | Ruby | 3.4.8+ (MSVC build) |
22
+ | OS | Windows 10/11 x64 |
23
+ | Compiler | Visual Studio 2022+ |
24
+ | Architecture | x64-mswin64_140 |
25
+
26
+ ## Installation
27
+
28
+ ```powershell
29
+ gem install oj_windows
30
+ ```
31
+
32
+ Or in your Gemfile:
33
+
34
+ ```ruby
35
+ gem 'oj_windows'
36
+ ```
37
+
38
+ ## Quick Start
39
+
40
+ ```ruby
41
+ require 'oj_windows'
42
+
43
+ # Dump Ruby objects to JSON
44
+ hash = { 'name' => 'oj_windows', 'version' => '3.17.2.1', 'windows' => true }
45
+ json = Oj.dump(hash)
46
+ # => {"name":"oj_windows","version":"3.16.14","windows":true}
47
+
48
+ # Load JSON back to Ruby objects
49
+ data = Oj.load(json)
50
+ # => {"name"=>"oj_windows", "version"=>"3.16.14", "windows"=>true}
51
+
52
+ # Pretty print
53
+ puts Oj.dump(hash, indent: 2)
54
+ ```
55
+
56
+ ## Features
57
+
58
+ - **SIMD-accelerated** C extension compiled with MSVC (SSE2 string scanning on x86_64)
59
+ - **Multiple Modes**: Strict, Compat, Object, Custom, Rails, WAB, Null
60
+ - **JSON Gem Compatibility**: Drop-in replacement via `Oj.mimic_JSON`
61
+ - **Rails Integration**: `:rails` mode and `Oj.optimize_rails` for ActiveSupport/ActiveRecord (see note under Test Results)
62
+ - **Streaming**: SAJ and SC parsers for large documents
63
+ - **Safe parsing**: `Oj::Parser.safe(max_depth:, max_array_size:, max_hash_size:, max_total_elements:)` enforces limits on untrusted input
64
+ - **StringWriter/StreamWriter**: Efficient JSON generation
65
+
66
+ ## Test Results
67
+
68
+ All tests pass on Windows MSVC Ruby 3.4.8:
69
+
70
+ | Test Suite | Runs | Assertions | Failures | Errors | Skips |
71
+ |------------|------|------------|----------|--------|-------|
72
+ | test_various.rb | 69 | 8,202 | 0 | 0 | 1 |
73
+ | test_strict.rb | 42 | 71 | 0 | 0 | 0 |
74
+ | test_compat.rb | 55 | 112 | 0 | 0 | 0 |
75
+ | test_object.rb | 74 | 161 | 0 | 0 | 0 |
76
+ | test_fast.rb | 43 | 137 | 0 | 0 | 0 |
77
+ | test_saj.rb | 13 | 18 | 0 | 0 | 0 |
78
+ | test_scp.rb | 23 | 23 | 0 | 0 | 2 |
79
+ | test_gc.rb | 3 | 50 | 0 | 0 | 0 |
80
+ | test_writer.rb | 27 | 28 | 0 | 0 | 1 |
81
+ | test_file.rb | 21 | 52 | 0 | 0 | 1 |
82
+ | test_hash.rb | 3 | 7 | 0 | 0 | 0 |
83
+ | **Total** | **373** | **8,861** | **0** | **0** | **5** |
84
+
85
+ > **Scope**: This is the core suite run by `test/test_all_no_rails.bat`. Additional mode/parser tests (`test_custom`, `test_null`, `test_wab`, `test_generate`, `test_parser_usual`, `test_integer_range`, `test_long_strings`) also pass. A few files depend on POSIX fixtures or a specific working directory (`test_realworld` needs `test/data/*.json`; `test_parser_saj`'s file test needs a local fixture) and are not part of this table. The Rails ActiveSupport/ActiveRecord suites require `fork` and are not run on Windows.
86
+ >
87
+ > **Skips (5)**: 3 require `fork` (`test_various` ×1, `test_scp` ×2), 1 needs a POSIX subprocess (`test_writer`), and 1 involves pre-1970 dates the Windows time API does not support (`test_file`).
88
+
89
+ ## Performance
90
+
91
+ `oj_windows` enables SIMD-accelerated string scanning on x86_64 (SSE2 by default —
92
+ see [InstallOptions](pages/InstallOptions.md)). On string-heavy payloads this makes
93
+ parsing roughly **20–25% faster** than the scalar build that earlier versions
94
+ shipped on MSVC.
95
+
96
+ A note on honest benchmarking: Ruby's bundled `JSON::Ext` is extremely well
97
+ optimized and is competitive with and on small, simple payloads can exceed —
98
+ `oj_windows` on raw parse/dump throughput. Oj's value is less about winning a
99
+ micro-benchmark and more about capabilities the JSON gem does not offer:
100
+
101
+ - **Object mode** full Ruby object serialization/deserialization
102
+ - **Rails / ActiveSupport** integration (`Oj.optimize_rails`)
103
+ - **Streaming** parsers (SAJ, SCP) for large documents
104
+ - **Custom** serialization behavior and circular-reference handling
105
+
106
+ Benchmark your own representative payloads with the scripts in `test/perf_*.rb`
107
+ rather than relying on synthetic numbers.
108
+
109
+ ## Modes
110
+
111
+ | Mode | Description |
112
+ |------|-------------|
113
+ | `:strict` | Strict JSON compliance, only native types |
114
+ | `:compat` | Compatible with the JSON gem |
115
+ | `:object` | Full Ruby object serialization |
116
+ | `:custom` | Customizable serialization behavior |
117
+ | `:rails` | Rails and ActiveSupport compatibility |
118
+ | `:wab` | WAB (Web Application Builder) format |
119
+ | `:null` | Return null for unsupported types |
120
+
121
+ ## Configuration
122
+
123
+ ```ruby
124
+ # Set default options
125
+ Oj.default_options = {
126
+ mode: :compat,
127
+ symbol_keys: true,
128
+ indent: 2
129
+ }
130
+
131
+ # Check current options
132
+ Oj.default_options
133
+ ```
134
+
135
+ ## Documentation
136
+
137
+ - [Options](pages/Options.md) - Parse and dump options
138
+ - [Modes](pages/Modes.md) - Detailed mode documentation
139
+ - [Rails](pages/Rails.md) - Rails integration guide
140
+ - [JsonGem](pages/JsonGem.md) - JSON gem compatibility
141
+ - [Advanced](pages/Advanced.md) - Advanced features
142
+
143
+ ## Building from Source
144
+
145
+ ```powershell
146
+ # Requires a Visual Studio (2019+) MSVC build environment. package_install.bat
147
+ # locates vcvars64.bat automatically via vswhere, so just run:
148
+ .\package_install.bat
149
+ ```
150
+
151
+ > If you prefer to set up the environment yourself, run the `vcvars64.bat` from
152
+ > your Visual Studio install (e.g. `...\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat`)
153
+ > first, then `bundle exec rake compile`.
154
+
155
+ ## Running Tests
156
+
157
+ ```powershell
158
+ cd test
159
+ .\test_all_no_rails.bat
160
+ ```
161
+
162
+ ## License
163
+
164
+ MIT License - See [LICENSE](LICENSE) for details.
165
+
166
+ ---
167
+
168
+ *Version 3.17.2.1 — based on [Oj](https://github.com/ohler55/oj) 3.17.2 by Peter Ohler (MIT).*
@@ -52,6 +52,13 @@ if enable_config('trace-log', false)
52
52
  dflags['OJ_ENABLE_TRACE_LOG'] = 1
53
53
  end
54
54
 
55
+ # SIMD string scanning is enabled by default (SSE2/SSE4.2 on x86_64 including
56
+ # MSVC, NEON on ARM). Build with `--disable-simd` to force the portable scalar
57
+ # scanner, e.g. `gem install oj_windows -- --disable-simd`.
58
+ unless enable_config('simd', true)
59
+ dflags['OJ_DISABLE_SIMD'] = 1
60
+ end
61
+
55
62
  dflags.each do |k, v|
56
63
  if v.nil?
57
64
  $CPPFLAGS += " -D#{k}"
@@ -70,8 +77,7 @@ end
70
77
 
71
78
  create_makefile(File.join(extension_name, extension_name))
72
79
 
73
- if is_windows
74
- %x{nmake clean}
75
- else
76
- %x{make clean}
77
- end
80
+ # NOTE: do not run `nmake clean` / `make clean` here. This file runs during
81
+ # `gem install` right before the build step, so cleaning is pointless on a fresh
82
+ # install (there is nothing to clean) and only emits confusing output. Use
83
+ # `rake clean` for local development instead.
data/ext/oj_windows/mem.c CHANGED
@@ -31,7 +31,21 @@ typedef struct _rep {
31
31
 
32
32
  #ifdef MEM_DEBUG
33
33
 
34
- static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
34
+ #if IS_WINDOWS
35
+ // Included here (after ruby.h, which already pulls in winsock2/windows on mswin)
36
+ // to avoid the winsock.h vs winsock2.h redefinition clash that occurs if
37
+ // <windows.h> is included ahead of Ruby's headers.
38
+ #include <windows.h>
39
+ // CRITICAL_SECTION has no static initializer, but SRWLOCK does (SRWLOCK_INIT)
40
+ // and is the natural Windows equivalent for this simple debug-only lock.
41
+ static SRWLOCK lock = SRWLOCK_INIT;
42
+ #define MEM_LOCK() AcquireSRWLockExclusive(&lock)
43
+ #define MEM_UNLOCK() ReleaseSRWLockExclusive(&lock)
44
+ #else
45
+ static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
46
+ #define MEM_LOCK() pthread_mutex_lock(&lock)
47
+ #define MEM_UNLOCK() pthread_mutex_unlock(&lock)
48
+ #endif
35
49
  static Rec recs = NULL;
36
50
  static const char mem_pad[] = "--- This is a memory pad and should not change until being freed. ---";
37
51
 
@@ -48,10 +62,10 @@ void *oj_malloc(size_t size, const char *file, int line) {
48
62
  r->file = file;
49
63
  r->line = line;
50
64
  r->ruby = false;
51
- pthread_mutex_lock(&lock);
65
+ MEM_LOCK();
52
66
  r->next = recs;
53
67
  recs = r;
54
- pthread_mutex_unlock(&lock);
68
+ MEM_UNLOCK();
55
69
  } else {
56
70
  free(ptr);
57
71
  ptr = NULL;
@@ -66,7 +80,7 @@ void *oj_realloc(void *orig, size_t size, const char *file, int line) {
66
80
 
67
81
  if (NULL != ptr) {
68
82
  strcpy(((char *)ptr) + size, mem_pad);
69
- pthread_mutex_lock(&lock);
83
+ MEM_LOCK();
70
84
  for (r = recs; NULL != r; r = r->next) {
71
85
  if (orig == r->ptr) {
72
86
  r->ptr = ptr;
@@ -77,7 +91,7 @@ void *oj_realloc(void *orig, size_t size, const char *file, int line) {
77
91
  break;
78
92
  }
79
93
  }
80
- pthread_mutex_unlock(&lock);
94
+ MEM_UNLOCK();
81
95
  if (NULL == r) {
82
96
  printf("Realloc at %s:%d (%p) not allocated.\n", file, line, orig);
83
97
  }
@@ -100,10 +114,10 @@ void *oj_calloc(size_t count, size_t size, const char *file, int line) {
100
114
  r->file = file;
101
115
  r->line = line;
102
116
  r->ruby = false;
103
- pthread_mutex_lock(&lock);
117
+ MEM_LOCK();
104
118
  r->next = recs;
105
119
  recs = r;
106
- pthread_mutex_unlock(&lock);
120
+ MEM_UNLOCK();
107
121
  } else {
108
122
  free(ptr);
109
123
  ptr = NULL;
@@ -125,10 +139,10 @@ void *oj_r_alloc(size_t size, const char *file, int line) {
125
139
  r->file = file;
126
140
  r->line = line;
127
141
  r->ruby = true;
128
- pthread_mutex_lock(&lock);
142
+ MEM_LOCK();
129
143
  r->next = recs;
130
144
  recs = r;
131
- pthread_mutex_unlock(&lock);
145
+ MEM_UNLOCK();
132
146
  } else {
133
147
  free(ptr);
134
148
  ptr = NULL;
@@ -143,7 +157,7 @@ void *oj_r_realloc(void *orig, size_t size, const char *file, int line) {
143
157
 
144
158
  if (NULL != ptr) {
145
159
  strcpy(((char *)ptr) + size, mem_pad);
146
- pthread_mutex_lock(&lock);
160
+ MEM_LOCK();
147
161
  for (r = recs; NULL != r; r = r->next) {
148
162
  if (orig == r->ptr) {
149
163
  r->ptr = ptr;
@@ -154,7 +168,7 @@ void *oj_r_realloc(void *orig, size_t size, const char *file, int line) {
154
168
  break;
155
169
  }
156
170
  }
157
- pthread_mutex_unlock(&lock);
171
+ MEM_UNLOCK();
158
172
  if (NULL == r) {
159
173
  printf("Realloc at %s:%d (%p) not allocated.\n", file, line, orig);
160
174
  }
@@ -167,7 +181,7 @@ void oj_freed(void *ptr, const char *file, int line, bool ruby) {
167
181
  Rec r = NULL;
168
182
  Rec prev = NULL;
169
183
 
170
- pthread_mutex_lock(&lock);
184
+ MEM_LOCK();
171
185
  for (r = recs; NULL != r; r = r->next) {
172
186
  if (ptr == r->ptr) {
173
187
  if (NULL == prev) {
@@ -179,7 +193,7 @@ void oj_freed(void *ptr, const char *file, int line, bool ruby) {
179
193
  }
180
194
  prev = r;
181
195
  }
182
- pthread_mutex_unlock(&lock);
196
+ MEM_UNLOCK();
183
197
  if (NULL == r) {
184
198
  printf("Free at %s:%d (%p) not allocated or already freed.\n", file, line, ptr);
185
199
  } else {
@@ -242,10 +256,10 @@ char *oj_mem_strdup(const char *str, const char *file, int line) {
242
256
  r->file = file;
243
257
  r->line = line;
244
258
  r->ruby = false;
245
- pthread_mutex_lock(&lock);
259
+ MEM_LOCK();
246
260
  r->next = recs;
247
261
  recs = r;
248
- pthread_mutex_unlock(&lock);
262
+ MEM_UNLOCK();
249
263
  } else {
250
264
  free(ptr);
251
265
  ptr = NULL;
@@ -281,7 +295,7 @@ static Rep update_reps(Rep reps, Rec r) {
281
295
 
282
296
  static void print_stats() {
283
297
  printf("\n--- Memory Usage Report --------------------------------------------------------\n");
284
- pthread_mutex_lock(&lock);
298
+ MEM_LOCK();
285
299
 
286
300
  if (NULL == recs) {
287
301
  printf("No memory leaks\n");
@@ -306,7 +320,7 @@ static void print_stats() {
306
320
  }
307
321
  printf("%lu bytes leaked\n", leaked);
308
322
  }
309
- pthread_mutex_unlock(&lock);
323
+ MEM_UNLOCK();
310
324
  printf("--------------------------------------------------------------------------------\n");
311
325
  }
312
326
 
data/ext/oj_windows/oj.h CHANGED
@@ -34,6 +34,12 @@ extern "C" {
34
34
  #if defined(_MSC_VER)
35
35
  #define __attribute__(x)
36
36
  #define timegm _mkgmtime
37
+ // MSVC has no native ssize_t (only SSIZE_T from <BaseTsd.h>, pulled in by
38
+ // <windows.h> above). Ruby's mswin headers normally define ssize_t/HAVE_SSIZE_T;
39
+ // provide a fallback only if they did not, so the extension never relies on it.
40
+ #if !defined(HAVE_SSIZE_T) && !defined(ssize_t)
41
+ typedef SSIZE_T ssize_t;
42
+ #endif
37
43
  #endif
38
44
  #else
39
45
  #define IS_WINDOWS 0