docs: public static Runnable writeSnapshotDigest(
          long txnId,
          long catalogCRC,
          String path,
          String pathType,
          String nonce,
          List<Table> tables,
          int hostId,
          Map<Integer, Long> partitionTransactionIds,
          ExtensibleSnapshotDigestData extraSnapshotData,
          InstanceId instanceId,
          long timestamp,
          int newPartitionCount,
          int clusterId,
          boolean isTruncationSnapshot)
      throws IOException
      {
          final File f = new VoltFile(path, constructDigestFilenameForNonce(nonce, hostId));
          if (f.exists()) {
              if (!f.delete()) {
                  throw new IOException("Unable to write table list file " + f);
              }
          }
          boolean success = false;
          try {
              final FileOutputStream fos = new FileOutputStream(f);
              StringWriter sw = new StringWriter();
              JSONStringer stringer = new JSONStringer();
              try {
                  stringer.object();
                  stringer.keySymbolValuePair("version", 1);
                  stringer.keySymbolValuePair("clusterid", clusterId);
                  stringer.keySymbolValuePair("txnId", txnId);
                  stringer.keySymbolValuePair("timestamp", timestamp);
                  stringer.keySymbolValuePair("timestampString", SnapshotUtil.formatHumanReadableDate(timestamp));
                  stringer.keySymbolValuePair(JSON_NEW_PARTITION_COUNT, newPartitionCount);
                  stringer.key("tables").array();
                  for (int ii = 0; ii < tables.size(); ii++) {
                      stringer.value(tables.get(ii).getTypeName());
                  }
                  stringer.endArray();
  
                  stringer.key("partitionTransactionIds").object();
                  for (Map.Entry<Integer, Long> entry : partitionTransactionIds.entrySet()) {
                      stringer.key(entry.getKey().toString()).value(entry.getValue());
                  }
                  stringer.endObject();
  
                  stringer.keySymbolValuePair("catalogCRC", catalogCRC);
                  stringer.key("instanceId").value(instanceId.serializeToJSONObject());
  
                  extraSnapshotData.writeToSnapshotDigest(stringer);
                  stringer.endObject();
              }
              catch (JSONException e) {
                  throw new IOException(e);
              }
  
              sw.append(stringer.toString());
  
              final byte tableListBytes[] = sw.getBuffer().toString().getBytes(StandardCharsets.UTF_8);
              final PureJavaCrc32 crc = new PureJavaCrc32();
              crc.update(tableListBytes);
              ByteBuffer fileBuffer = ByteBuffer.allocate(tableListBytes.length + 4);
              fileBuffer.putInt((int)crc.getValue());
              fileBuffer.put(tableListBytes);
              fileBuffer.flip();
              fos.getChannel().write(fileBuffer);
              success = true;
              return new Runnable() {
                  @Override
                  public void run() {
                      try {
                          fos.getChannel().force(true);
                      }
                      catch (IOException e) {
                          if (isTruncationSnapshot) {
                              VoltDB.crashLocalVoltDB("Unexpected exception while attempting to create digest for truncation snapshot",
                                      true, e);
                          }
                          throw new RuntimeException(e);
                      }
                      finally {
                          try {
                              fos.close();
                          }
                          catch (IOException e) {
                              if (isTruncationSnapshot) {
                                  VoltDB.crashLocalVoltDB("Unexpected exception while attempting to create digest for truncation snapshot",
                                          true, e);
                              }
                              throw new RuntimeException(e);
                          }
                      }
                  }
              };
          }
          finally {
              if (!success) {
                  f.delete();
              }
          }
      }