Merge branch 'master' into master
diff --git a/.github/workflows/build_test.yml b/.github/workflows/build_test.yml
index e0f4bd3..90dda94 100644
--- a/.github/workflows/build_test.yml
+++ b/.github/workflows/build_test.yml
@@ -218,7 +218,7 @@
     steps:
 
     - name: Harden Runner
-      uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
+      uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
       with:
         egress-policy: audit
 
@@ -332,7 +332,7 @@
         # cd integration
         # mvn -B verify
 
-    - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
+    - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
       if: ${{ matrix.build_system == 'python' }}
       with:
         python-version: ${{ matrix.python_version }}
diff --git a/.github/workflows/build_test_wasm.yml b/.github/workflows/build_test_wasm.yml
index da24387..ead2f21 100644
--- a/.github/workflows/build_test_wasm.yml
+++ b/.github/workflows/build_test_wasm.yml
@@ -35,7 +35,7 @@
     steps:
 
     - name: Harden Runner
-      uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
+      uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
       with:
         egress-policy: audit
 
@@ -53,7 +53,7 @@
       run: which node >> $HOME/.base_node_path
 
     - name: Install emsdk
-      uses: mymindstorm/setup-emsdk@6ab9eb1bda2574c4ddb79809fc9247783eaf9021 # v14
+      uses: mymindstorm/setup-emsdk@667eb33f24e84e7f362c16d8d7fff0629a73e15e # v15
       with:
         version: ${{env.EM_VERSION}}
         no-cache: true
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index 7a4aa40..f80e7d0 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -35,7 +35,7 @@
     steps:
 
     - name: Harden Runner
-      uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
+      uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
       with:
         egress-policy: audit
 
@@ -44,7 +44,7 @@
 
     # Initializes the CodeQL tools for scanning.
     - name: Initialize CodeQL
-      uses: github/codeql-action/init@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # v3.29.5
+      uses: github/codeql-action/init@c10b8064de6f491fea524254123dbe5e09572f13 # v3.29.5
       with:
         languages: ${{ matrix.language }}
         # CodeQL is currently crashing on files with large lists:
@@ -56,7 +56,7 @@
 
     - if: matrix.language == 'cpp'
       name: Build CPP
-      uses: github/codeql-action/autobuild@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # v3.29.5
+      uses: github/codeql-action/autobuild@c10b8064de6f491fea524254123dbe5e09572f13 # v3.29.5
 
     - if: matrix.language == 'cpp' || matrix.language == 'java'
       name: Build Java
@@ -66,7 +66,7 @@
 
     - if: matrix.language == 'javascript'
       name: Build JS
-      uses: github/codeql-action/autobuild@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # v3.29.5
+      uses: github/codeql-action/autobuild@c10b8064de6f491fea524254123dbe5e09572f13 # v3.29.5
 
     - if: matrix.language == 'cpp' || matrix.language == 'python'
       name: Build Python
@@ -76,7 +76,7 @@
         python setup.py build_ext
 
     - name: Perform CodeQL Analysis
-      uses: github/codeql-action/analyze@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # v3.29.5
+      uses: github/codeql-action/analyze@c10b8064de6f491fea524254123dbe5e09572f13 # v3.29.5
       with:
         category: "/language:${{matrix.language}}"
         ref: "${{ github.ref != 'master' && github.ref || '/refs/heads/master' }}"
diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml
index c1ee09d..a1734e4 100644
--- a/.github/workflows/fuzz.yml
+++ b/.github/workflows/fuzz.yml
@@ -22,7 +22,7 @@
     steps:
 
     - name: Harden Runner
-      uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
+      uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
       with:
         egress-policy: audit
 
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 8d60a65..cecf380 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -30,7 +30,7 @@
     steps:
 
     - name: Harden Runner
-      uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
+      uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
       with:
         egress-policy: audit
 
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index a995b94..ead2bea 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -64,7 +64,7 @@
     steps:
 
     - name: Harden Runner
-      uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
+      uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
       with:
         egress-policy: audit
 
@@ -145,7 +145,7 @@
     steps:
 
     - name: Harden Runner
-      uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
+      uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
       with:
         egress-policy: audit
 
@@ -182,7 +182,7 @@
         fetch-depth: 1
 
     - name: Download all artifacts
-      uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
+      uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
       with:
         path: release_assets
         merge-multiple: true
@@ -203,7 +203,7 @@
     steps:
 
     - name: Harden Runner
-      uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
+      uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
       with:
         egress-policy: audit
 
diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml
index 9e00961..121d36a 100644
--- a/.github/workflows/scorecard.yml
+++ b/.github/workflows/scorecard.yml
@@ -37,7 +37,7 @@
 
     steps:
       - name: Harden Runner
-        uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
+        uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1
         with:
           egress-policy: audit
 
@@ -77,6 +77,6 @@
 
       # Upload the results to GitHub's code scanning dashboard.
       - name: "Upload to code-scanning"
-        uses: github/codeql-action/upload-sarif@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # v2.23.3
+        uses: github/codeql-action/upload-sarif@c10b8064de6f491fea524254123dbe5e09572f13 # v2.23.3
         with:
           sarif_file: results.sarif
diff --git a/c/common/platform.h b/c/common/platform.h
index e1254d5..8080cbb 100644
--- a/c/common/platform.h
+++ b/c/common/platform.h
@@ -24,6 +24,7 @@
 #ifndef BROTLI_COMMON_PLATFORM_H_
 #define BROTLI_COMMON_PLATFORM_H_
 
+#include <limits.h>
 #include <string.h>  /* IWYU pragma: export memcmp, memcpy, memset */
 #include <stdlib.h>  /* IWYU pragma: export exit, free, malloc */
 #include <sys/types.h>  /* should include endian.h for us */
@@ -522,6 +523,14 @@
 #define BROTLI_MIN(T, A, B) (brotli_min_ ## T((A), (B)))
 #define BROTLI_MAX(T, A, B) (brotli_max_ ## T((A), (B)))
 
+static BROTLI_INLINE int brotli_safe_add_int(int a, int b, int* result) {
+  if (b > 0 && a > INT_MAX - b) return 0;
+  if (b < 0 && a < INT_MIN - b) return 0;
+  *result = a + b;
+  return 1;
+}
+#define BROTLI_SAFE_ADD(T, A, B, R) (brotli_safe_add_ ## T((A), (B), (R)))
+
 #define BROTLI_SWAP(T, A, I, J) { \
   T __brotli_swap_tmp = (A)[(I)]; \
   (A)[(I)] = (A)[(J)];            \
@@ -607,6 +616,7 @@
   BROTLI_UNUSED(&brotli_max_uint32_t);
   BROTLI_UNUSED(&brotli_min_uint8_t);
   BROTLI_UNUSED(&brotli_max_uint8_t);
+  BROTLI_UNUSED(&brotli_safe_add_int);
   BROTLI_UNUSED(&BrotliDefaultAllocFunc);
   BROTLI_UNUSED(&BrotliDefaultFreeFunc);
   BROTLI_UNUSED(&BrotliRotateRight16);
@@ -615,6 +625,7 @@
 #if BROTLI_ENABLE_DUMP
   BROTLI_UNUSED(&BrotliDump);
 #endif
+}
 
 #if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_I86)) && \
     !defined(_M_ARM64EC)
@@ -654,7 +665,6 @@
 #elif defined(BROTLI_TZCNT64)
 #define BROTLI_MAX_SIMD_QUALITY 6
 #endif
-}
 
 #if defined(_MSC_VER)
 #define BROTLI_CRASH() __debugbreak(), (void)abort()
diff --git a/c/dec/decode.c b/c/dec/decode.c
index 90cce9d..5510d10 100644
--- a/c/dec/decode.c
+++ b/c/dec/decode.c
@@ -1526,6 +1526,8 @@
 static BROTLI_BOOL AttachCompoundDictionary(
     BrotliDecoderState* state, const uint8_t* data, size_t size) {
   BrotliDecoderCompoundDictionary* addon = state->compound_dictionary;
+  int new_size = (int)size;
+  if (new_size < 0 || (size_t)new_size != size) return BROTLI_FALSE;
   if (state->state != BROTLI_STATE_UNINITED) return BROTLI_FALSE;
   if (!addon) {
     addon = (BrotliDecoderCompoundDictionary*)BROTLI_DECODER_ALLOC(
@@ -1540,10 +1542,13 @@
     state->compound_dictionary = addon;
   }
   if (addon->num_chunks == 15) return BROTLI_FALSE;
+  if (!BROTLI_SAFE_ADD(int, addon->total_size, new_size, &new_size)) {
+    return BROTLI_FALSE;
+  }
   addon->chunks[addon->num_chunks] = data;
   addon->num_chunks++;
-  addon->total_size += (int)size;
-  addon->chunk_offsets[addon->num_chunks] = addon->total_size;
+  addon->total_size = new_size;
+  addon->chunk_offsets[addon->num_chunks] = new_size;
   return BROTLI_TRUE;
 }
 
diff --git a/java/org/brotli/dec/Decode.java b/java/org/brotli/dec/Decode.java
index c4dde3f..d2c71b1 100644
--- a/java/org/brotli/dec/Decode.java
+++ b/java/org/brotli/dec/Decode.java
@@ -224,7 +224,7 @@
   /**
    * Reads brotli stream header and parses "window bits".
    *
-   * @param s initialized state, before any read is performed.
+   * @param s initialized state, before any read is performed
    * @return -1 if header is invalid
    */
   private static int decodeWindowBits(State s) {
@@ -272,7 +272,8 @@
    *
    * In "eager" mode decoder returns as soon as there is enough data to fill output buffer.
    *
-   * @param s initialized state, before any read is performed.
+   * @param s initialized state, before any read is performed
+   * @return {@link BROTLI_OK} on success; otherwise an error code
    */
   static int enableEagerOutput(State s) {
     if (s.runningState != INITIALIZED) {
diff --git a/java/org/brotli/dec/Huffman.java b/java/org/brotli/dec/Huffman.java
index 173075d..25fb622 100644
--- a/java/org/brotli/dec/Huffman.java
+++ b/java/org/brotli/dec/Huffman.java
@@ -40,9 +40,10 @@
   }
 
   /**
-   * @param count histogram of bit lengths for the remaining symbols,
-   * @param len code length of the next processed symbol.
-   * @return table width of the next 2nd level table.
+   * @param count histogram of bit lengths for the remaining symbols
+   * @param len code length of the next processed symbol
+   * @param rootBits number of bits in the root table
+   * @return table width of the next 2nd level table
    */
   private static int nextTableBitSize(int[] count, int len, int rootBits) {
     int bits = len;
diff --git a/java/org/brotli/integration/BundleChecker.java b/java/org/brotli/integration/BundleChecker.java
index b03c1ae..3be98ef 100644
--- a/java/org/brotli/integration/BundleChecker.java
+++ b/java/org/brotli/integration/BundleChecker.java
@@ -31,7 +31,7 @@
   private final boolean sanityCheck;
 
   /**
-   * @param sanityCheck do not calculate checksum and ignore {@link IOException}.
+   * @param sanityCheck do not calculate checksum and ignore {@link IOException}
    */
   public BundleChecker(InputStream input, AtomicInteger nextJob, boolean sanityCheck) {
     this.input = input;
diff --git a/java/org/brotli/wrapper/enc/BrotliOutputStream.java b/java/org/brotli/wrapper/enc/BrotliOutputStream.java
index 10595d1..fe3596d 100644
--- a/java/org/brotli/wrapper/enc/BrotliOutputStream.java
+++ b/java/org/brotli/wrapper/enc/BrotliOutputStream.java
@@ -18,6 +18,7 @@
   /** The default internal buffer size used by the encoder. */
   private static final int DEFAULT_BUFFER_SIZE = 16384;
 
+  private final Object lock = new Object();
   private final Encoder encoder;
 
   /**
@@ -42,31 +43,39 @@
   }
 
   public void attachDictionary(PreparedDictionary dictionary) throws IOException {
-    encoder.attachDictionary(dictionary);
+    synchronized (lock) {
+      encoder.attachDictionary(dictionary);
+    }
   }
 
   @Override
   public void close() throws IOException {
-    encoder.close();
+    synchronized (lock) {
+      encoder.close();
+    }
   }
 
   @Override
   public void flush() throws IOException {
-    if (encoder.closed) {
-      throw new IOException("write after close");
+    synchronized (lock) {
+      if (encoder.closed) {
+        throw new IOException("write after close");
+      }
+      encoder.flush();
     }
-    encoder.flush();
   }
 
   @Override
   public void write(int b) throws IOException {
-    if (encoder.closed) {
-      throw new IOException("write after close");
+    synchronized (lock) {
+      if (encoder.closed) {
+        throw new IOException("write after close");
+      }
+      while (!encoder.encode(EncoderJNI.Operation.PROCESS)) {
+        // Busy-wait loop.
+      }
+      encoder.inputBuffer.put((byte) b);
     }
-    while (!encoder.encode(EncoderJNI.Operation.PROCESS)) {
-      // Busy-wait loop.
-    }
-    encoder.inputBuffer.put((byte) b);
   }
 
   @Override
@@ -76,17 +85,19 @@
 
   @Override
   public void write(byte[] b, int off, int len) throws IOException {
-    if (encoder.closed) {
-      throw new IOException("write after close");
-    }
-    while (len > 0) {
-      if (!encoder.encode(EncoderJNI.Operation.PROCESS)) {
-        continue;
+    synchronized (lock) {
+      if (encoder.closed) {
+        throw new IOException("write after close");
       }
-      int limit = Math.min(len, encoder.inputBuffer.remaining());
-      encoder.inputBuffer.put(b, off, limit);
-      off += limit;
-      len -= limit;
+      while (len > 0) {
+        if (!encoder.encode(EncoderJNI.Operation.PROCESS)) {
+          continue;
+        }
+        int limit = Math.min(len, encoder.inputBuffer.remaining());
+        encoder.inputBuffer.put(b, off, limit);
+        off += limit;
+        len -= limit;
+      }
     }
   }
 }
diff --git a/java/org/brotli/wrapper/enc/Encoder.java b/java/org/brotli/wrapper/enc/Encoder.java
index ab0e5aa..eb929bc 100644
--- a/java/org/brotli/wrapper/enc/Encoder.java
+++ b/java/org/brotli/wrapper/enc/Encoder.java
@@ -69,6 +69,7 @@
      * Setup encoder quality.
      *
      * @param quality compression quality, or -1 for default
+     * @return this
      */
     public Parameters setQuality(int quality) {
       if (quality < -1 || quality > 11) {
@@ -82,6 +83,7 @@
      * Setup encoder window size.
      *
      * @param lgwin log2(LZ window size), or -1 for default
+     * @return this
      */
     public Parameters setWindow(int lgwin) {
       if ((lgwin != -1) && ((lgwin < 10) || (lgwin > 24))) {