text:
package net.sourceforge.pebble.web.action;

import net.sourceforge.pebble.Constants;
import net.sourceforge.pebble.domain.SingleBlogTestCase;
import net.sourceforge.pebble.mock.MockHttpServletRequest;
import net.sourceforge.pebble.mock.MockHttpServletResponse;
import net.sourceforge.pebble.web.model.Model;

/**
 * Extends BlogTestCase, adding setup logic to easily test secure actions.
 *
 * @author    Simon Brown
 */
public abstract class SecureActionTestCase extends SingleBlogTestCase {

  protected SecureAction action;
  protected MockHttpServletRequest request;
  protected MockHttpServletResponse response;
  protected Model model;

  protected void setUp() throws Exception {
    super.setUp();

    request = new MockHttpServletRequest();
    response = new MockHttpServletResponse();

    model = new Model();
    model.put(Constants.BLOG_KEY, blog);
    action.setModel(model);
  }

}

redpajama_set_name: RedPajamaGithub


text:
package com.github.ajanthan.lightrest.servlet.response;

import com.github.ajanthan.lightrest.http.LightRestResponseContext;
import com.github.ajanthan.lightrest.http.providers.ProviderFactory;
import com.github.ajanthan.lightrest.http.response.EntityContext;
import com.github.ajanthan.lightrest.http.response.LightRestServerErrorMessage;
import com.github.ajanthan.lightrest.http.response.ResponseWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.MessageBodyWriter;
import java.io.IOException;
import java.io.OutputStream;

/**
 * Created by ajanthan on 7/2/16.
 */
public class DefaultResponseWriter implements ResponseWriter {
    private static final Logger log = LoggerFactory.getLogger(DefaultResponseWriter.class);
    private HttpServletResponse httpServletResponse;
    private ProviderFactory providerFactory;

    public DefaultResponseWriter(HttpServletResponse httpServletResponse) {
        this.httpServletResponse = httpServletResponse;
        this.providerFactory = ProviderFactory.getIntance();
    }

    @Override
    public void writeResponse(LightRestResponseContext restResponseContext) {
        int responseStatus = restResponseContext.getStatus();
        EntityContext entityContext = restResponseContext.getEntityContext();

        if (entityContext.getEntityClass().equals(Response.class)) {
            //handle response object
            Response response = (Response) entityContext.getEntityObject();
            responseStatus = response.getStatus();
            if (response.getEntity() != null) {
                entityContext.setEntityObject(response.getEntity());
                entityContext.setEntityClass(response.getEntity().getClass());
                entityContext.setEntityType(response.getEntity().getClass());
            } else {
                entityContext = null;
                log.debug("Response entity is null for Response object");
            }
            log.debug("handled Response object successfully");
        }

        if (responseStatus > 0) {
            log.debug("Setting response status code {}", responseStatus);
            httpServletResponse.setStatus(responseStatus);
        } else {
            httpServletResponse.setStatus(200);
            log.debug("Setting default response status code 200");
        }
        if (entityContext == null) {
            return;

        } else if (entityContext.getEntityObject() == null) {
            return;
        }
        MediaType responseType = restResponseContext.getMediaType();

        MessageBodyWriter writer = providerFactory.getMessageBodyWriter(entityContext.getEntityClass(), entityContext.getEntityType(), null, responseType);
        if (writer != null) {
            OutputStream outputStream = null;
            try {
                outputStream = httpServletResponse.getOutputStream();
                httpServletResponse.setContentType(responseType.toString());
                writer.writeTo(entityContext.getEntityObject(), entityContext.getEntityClass(), entityContext.getEntityType(), null, responseType, null, outputStream);
                log.debug("Handled response with {} for {}", writer, responseType);
            } catch (IOException e) {

                String msg = "Error occurred while writing the response to outputstream ";
                log.error(msg, e);
                LightRestServerErrorMessage errorMessage = new LightRestServerErrorMessage();
                errorMessage.setErrorMessage(msg);
                errorMessage.setDetailMessage(e.getMessage());
                try {
                    writer.writeTo(entityContext.getEntityObject(), entityContext.getEntityClass(), entityContext.getEntityType(), null, responseType, null, outputStream);
                } catch (IOException e1) {
                    log.error("Error in writing error message into response", e);
                    try {
                        httpServletResponse.sendError(500, "Error in writing error message into response");
                    } catch (IOException e2) {
                        //ignore
                    }
                }

            } finally {
                try {
                    outputStream.flush();
                    outputStream.close();
                } catch (IOException e) {
                    //ignore
                }
            }
        } else {
            log.debug("Appropriate response writer is not found");
        }

    }
}


text:
title: Componente Menu React
components: Menu, MenuItem, MenuList, ClickAwayListener, Popover, Popper
---

# Menus

Os menus exibem uma lista de opções em superfícies temporárias.

O [Menu](https://material.io/design/components/menus.html) exibe uma lista de opções em uma superfície temporária. Aparece quando o usuário interage com um botão ou outro controle.

## Menu simples

Menus simples abrem sobre o elemento âncora por padrão (esta opção pode ser alterada via props). Quando estão perto de uma borda da tela, menus simples realinham verticalmente para garantir que todos os itens do menu fiquem completamente visíveis.

Escolhendo uma opção deve confirmar imediatamente a opção e fechar o menu.

**Desambiguação**: Em contraste com menus simples, uma caixa de diálogo simples pode apresentar detalhes adicionais relacionados às opções disponíveis para um item da lista ou fornecer navegação ou ações indiretas relacionada à tarefa principal. Embora possam exibir o mesmo conteúdo, menus simples são preferidos em relação a caixas de diálogos simples, pois menus simples são menos prejudiciais ao contexto atual do usuário.

{{"demo": "pages/components/menus/SimpleMenu.js"}}

## Menus Selecionados

Se usado para a seleção de itens, quando abertos, menus simples tentam alinhar verticalmente o item de menu atualmente selecionado com o elemento de âncora, e o foco inicial será colocado no item de menu selecionado. O item de menu atualmente selecionado é definido usando a propriedadeselected`(de ListItem).
Para usar um item de menu selecionado sem afetar o foco inicial ou o posicionamento vertical do menu, defina a propriedade variant` como `menu`.

{{"demo": "pages/components/menus/SimpleListMenu.js"}}

## Composição de MenuList

O componente `Menu` usa o componente `Popover` internamente. No entanto, você pode querer usar uma estratégia de posicionamento diferente ou não bloquear a rolagem. Para responder a essas necessidades, expomos um componente `MenuList` que você pode compor, com `Popper` neste exemplo.

A principal responsabilidade do componente `MenuList` é manipular o foco.

{{"demo": "pages/components/menus/MenuListComposition.js"}}

## Menus Customizados

Aqui está um exemplo de customização do componente. Você pode aprender mais sobre isso na [página de documentação de sobrescritas](/customization/components/).

{{"demo": "pages/components/menus/CustomizedMenus.js"}}

O `MenuItem` é um encapsulador em torno de `ListItem` com alguns estilos adicionais. Você pode usar os mesmos recursos de composição de lista com o componente `MenuItem`:

## Altura máxima dos menus

Se a altura de um menu impede que todos os itens de menu sejam exibidos, o menu terá internamente a opção de rolagem.

{{"demo": "pages/components/menus/LongMenu.js"}}

## Limitações

Existe [um erro com flexbox](https://bugs.chromium.org/p/chromium/issues/detail?id=327437) que impede `text-overflow: ellipsis` de funcionar em um leiaute flexbox. Você pode usar o componente `Typography` com `noWrap` para solucionar esse problema:

{{"demo": "pages/components/menus/TypographyMenu.js"}}

## Trocar transição

Use uma transição diferente.

{{"demo": "pages/components/menus/FadeMenu.js"}}

## Projetos Complementares

Para usos mais avançados, você pode tirar vantagem com:

### PopupState helper

Existe um pacote de terceiros [`material-ui-popup-state`](https://github.com/jcoreio/material-ui-popup-state) que cuida do estado do menu para você na maioria dos casos.

{{"demo": "pages/components/menus/MenuPopupState.js"}}


text:
START_ATF_NAMESPACE
    typedef tagDDDEVICEIDENTIFIER *LPDDDEVICEIDENTIFIER;
END_ATF_NAMESPACE


text:
using System;
using Umbraco.Core;
using Umbraco.Core.Cache;

namespace Umbraco.Web.Cache
{
    /// <summary>
    /// Handles Application cache invalidation/refreshing
    /// </summary>
    public sealed class ApplicationCacheRefresher : CacheRefresherBase<ApplicationCacheRefresher>
    {
        protected override ApplicationCacheRefresher Instance
        {
            get { return this; }
        }

        public override Guid UniqueIdentifier
        {
            get { return Guid.Parse(DistributedCache.ApplicationCacheRefresherId); }
        }

        public override string Name
        {
            get { return "Applications cache refresher"; }
        }

        public override void RefreshAll()
        {
            ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheItem(CacheKeys.ApplicationsCacheKey);
            base.RefreshAll();
        }

        public override void Refresh(int id)
        {
            Remove(id);
            base.Refresh(id);
        }

        public override void Remove(int id)
        {
            ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheItem(CacheKeys.ApplicationsCacheKey);
            base.Remove(id);
        }

    }
}


text:
import tensorflow as tf
from .common import layer_register, VariableHolder
from ..tfutils.common import get_tf_version_tuple
from ..utils.argtools import shape2d, shape4d, get_data_format
from .tflayer import rename_get_variable, convert_to_tflayer_args

__all__ = ['Conv2D', 'Deconv2D', 'Conv2DTranspose']

@layer_register(log_shape=True)
@convert_to_tflayer_args(
    args_names=['filters', 'kernel_size'],
    name_mapping={
        'out_channel': 'filters',
        'kernel_shape': 'kernel_size',
        'stride': 'strides',
    })
def Conv2D(
        inputs,
        filters,
        kernel_size,
        strides=(1, 1),
        padding='same',
        data_format='channels_last',
        dilation_rate=(1, 1),
        activation=None,
        use_bias=True,
        kernel_initializer=tf.contrib.layers.variance_scaling_initializer(2.0),
        bias_initializer=tf.zeros_initializer(),
        kernel_regularizer=None,
        bias_regularizer=None,
        activity_regularizer=None,
        split=1):
    """
    A wrapper around `tf.layers.Conv2D`.
    Some differences to maintain backward-compatibility:

    1. Default kernel initializer is variance_scaling_initializer(2.0).
    2. Default padding is 'same'.
    3. Support 'split' argument to do group conv.

    Variable Names:

    * ``W``: weights
    * ``b``: bias
    """
    if split == 1:
        with rename_get_variable({'kernel': 'W', 'bias': 'b'}):
            layer = tf.layers.Conv2D(
                filters,
                kernel_size,
                strides=strides,
                padding=padding,
                data_format=data_format,
                dilation_rate=dilation_rate,
                activation=activation,
                use_bias=use_bias,
                kernel_initializer=kernel_initializer,
                bias_initializer=bias_initializer,
                kernel_regularizer=kernel_regularizer,
                bias_regularizer=bias_regularizer,
                activity_regularizer=activity_regularizer,
                _reuse=tf.get_variable_scope().reuse)
            ret = layer.apply(inputs, scope=tf.get_variable_scope())
            ret = tf.identity(ret, name='output')

        ret.variables = VariableHolder(W=layer.kernel)
        if use_bias:
            ret.variables.b = layer.bias

    else:
        # group conv implementation
        data_format = get_data_format(data_format, tfmode=False)
        in_shape = inputs.get_shape().as_list()
        channel_axis = 3 if data_format == 'NHWC' else 1
        in_channel = in_shape[channel_axis]
        assert in_channel is not None, "[Conv2D] Input cannot have unknown channel!"
        assert in_channel % split == 0

        assert kernel_regularizer is None and bias_regularizer is None and activity_regularizer is None, \
            "Not supported by group conv now!"

        out_channel = filters
        assert out_channel % split == 0
        assert dilation_rate == (1, 1) or get_tf_version_tuple() >= (1, 5), 'TF>=1.5 required for group dilated conv'

        kernel_shape = shape2d(kernel_size)
        filter_shape = kernel_shape + [in_channel / split, out_channel]
        stride = shape4d(strides, data_format=data_format)

        kwargs = dict(data_format=data_format)
        if get_tf_version_tuple() >= (1, 5):
            kwargs['dilations'] = shape4d(dilation_rate, data_format=data_format)

        W = tf.get_variable(
            'W', filter_shape, initializer=kernel_initializer)

        if use_bias:
            b = tf.get_variable('b', [out_channel], initializer=bias_initializer)

        inputs = tf.split(inputs, split, channel_axis)
        kernels = tf.split(W, split, 3)
        outputs = [tf.nn.conv2d(i, k, stride, padding.upper(), **kwargs)
                   for i, k in zip(inputs, kernels)]
        conv = tf.concat(outputs, channel_axis)
        if activation is None:
            activation = tf.identity
        ret = activation(tf.nn.bias_add(conv, b, data_format=data_format) if use_bias else conv, name='output')

        ret.variables = VariableHolder(W=W)
        if use_bias:
            ret.variables.b = b
    return ret

@layer_register(log_shape=True)
@convert_to_tflayer_args(
    args_names=['filters', 'kernel_size', 'strides'],
    name_mapping={
        'out_channel': 'filters',
        'kernel_shape': 'kernel_size',
        'stride': 'strides',
    })
def Conv2DTranspose(
        inputs,
        filters,
        kernel_size,
        strides=(1, 1),
        padding='same',
        data_format='channels_last',
        activation=None,
        use_bias=True,
        kernel_initializer=tf.contrib.layers.variance_scaling_initializer(2.0),
        bias_initializer=tf.zeros_initializer(),
        kernel_regularizer=None,
        bias_regularizer=None,
        activity_regularizer=None):
    """
    A wrapper around `tf.layers.Conv2DTranspose`.
    Some differences to maintain backward-compatibility:

    1. Default kernel initializer is variance_scaling_initializer(2.0).
    2. Default padding is 'same'

    Variable Names:

    * ``W``: weights
    * ``b``: bias
    """

    with rename_get_variable({'kernel': 'W', 'bias': 'b'}):
        layer = tf.layers.Conv2DTranspose(
            filters,
            kernel_size,
            strides=strides,
            padding=padding,
            data_format=data_format,
            activation=activation,
            use_bias=use_bias,
            kernel_initializer=kernel_initializer,
            bias_initializer=bias_initializer,
            kernel_regularizer=kernel_regularizer,
            bias_regularizer=bias_regularizer,
            activity_regularizer=activity_regularizer,
            _reuse=tf.get_variable_scope().reuse)
        ret = layer.apply(inputs, scope=tf.get_variable_scope())
        ret = tf.identity(ret, name='output')

    ret.variables = VariableHolder(W=layer.kernel)
    if use_bias:
        ret.variables.b = layer.bias
    return ret

Deconv2D = Conv2DTranspose


text:
/** @jsx React.DOM */
var React = require('react');
var AppActions = require("../actions/app-actions");
var Store = require("../stores/app-store");

var BoidFlock = require('../libs/BoidFlock');

var MainPage = React.createClass({
    proptypes:{},

    handleClick:function(){
        AppActions.testAction(1);
    },
    componentDidMount:function(){
        Store.addChangeListener(this.onChange)
    },
    onChange:function(){
            //-- not currently needed, just a dummy callback
    },
    render:function(){
        return

            <MainPageBlurb />

    }
})

var MainPageBlurb = React.createClass({
    getInitialState:function(){
        return {topOffset: 100 }
    },
    getLeftOffset:function(){
        return (Store.getScreenSize().width - this.getWidthStyle() )/2
    },
    getOffset:function(){
        return "translate("+this.getLeftOffset()+"px,"+this.state.topOffset+"px)"
    },
    configs:[
        {   topOffset:1000,
            leftOffset:100,
            opacity:0,
            animationDelays:[500, 1000, 1500, 2000],
            leftDest:0,
            topDest:0,
            copy:"Hello, I'm Graham"
        },
        {   topOffset:1000,
            leftOffset:100,
            opacity:0,
            animationDelays:[1000, 1500, 2000, 2500],
            leftDest:0,
            topDest:20,
            copy:"I'm a software engineer"
        },
        {   topOffset:1000,
            leftOffset:100,
            opacity:0,
            animationDelays:[1500, 2000, 2500, 3000],
            leftDest:0,
            topDest:40,
            copy:"This is my site."
        },
        {   topOffset:1000,
            leftOffset:100,
            opacity:0,
            animationDelays:[1500, 2000, 2500, 3000],
            leftDest:0,
            topDest:60,
            copy:"Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."
        }
    ],
    componentDidMount:function(){
        Store.addChangeListener(this.onChange)
        this.onChange();
        console.log("Main did mount")
    },
    componentWillUnmount:function(){
        Store.removeChangeListener(this.onChange)
    },
    onChange:function(){
        console.log("Store.getScreenSize().height = ", Store.getScreenSize().height)
        this.setState({ 'topOffset': Store.getScreenSize().height/5 })
    },
    getWidthStyle:function(){
        return Store.getScreenSize().width > 1000 ? 700 : Store.getScreenSize().width/2;
    },
    getStyle:function(){
        return {
            "MsTransform": this.getOffset(),
            "WebkitTransform": this.getOffset(),
            "transform": this.getOffset(),
            "color": "#cccccc",
            "fontSize":"18px",
            "width":this.getWidthStyle()+"px",
            "WebkitTransition": ".5s ease-in-out",
            "MozTransition": ".5s ease-in-out",
            "OTransition": ".5s ease-in-out",
            "transition": ".5s ease-in-out"
        }
    },
    render:function(){
        return

        {
           this.configs.map(function(d,i){
                return <IntroCell config={d} />
            })
        }

    }
})

/*
This is the individula cell
*/

var IntroCell = React.createClass({
    getInitialState:function(){
        return {topOffset:1000,
            leftOffset:100,
            width:"100%",
            opacity:0,
            animationDelays:[500, 1000, 1500, 2000],
            leftDest:200,
            topDest:400,
            copy:"This is the default copy."
        }
    },
    getDefaultProps:function(){
        return{ config:{}}
    },
    componentWillMount:function(){
        for(var prop in this.props.config){
            this.state[prop] = this.props.config[prop]
        }
    },
    getOffset:function(){
        return "translate("+this.state.leftOffset+"px,"+this.state.topOffset+"px)"
    },
    getOpacity:function(){
        return this.state.opacity;
    },
    setOpacity:function(value){
        if(isNaN(value)){return};
         this.setState({"opacity":value});
    },
    setTopOffset:function(value){
        if(isNaN(value)) return;
        this.setState({'topOffset':value})
    },
    setLeftOffset:function(value){
        if(isNaN(value)) return;
        this.setState({'leftOffset':value})
    },
    getStyle:function(){
        return {
            "MsTransform": this.getOffset(),
            "WebkitTransform": this.getOffset(),
            "transform": this.getOffset(),
            "color": "#cccccc",
            "fontSize":"20px",
            "width":"100%",
            "opacity":this.getOpacity(),
            "WebkitTransition": ".5s ease-in-out",
            "MozTransition": ".5s ease-in-out",
            "OTransition": ".5s ease-in-out",
            "transition": ".5s ease-in-out"
        }
    },
    componentDidMount:function(){
        this.state.ani0 = setTimeout(this.anim0, this.state.animationDelays[0])
        this.state.ani1 = setTimeout(this.anim1, this.state.animationDelays[1])
        this.state.ani2 = setTimeout(this.anim2, this.state.animationDelays[2])
        this.state.ani3 = setTimeout(this.anim3, this.state.animationDelays[3])
    },
    anim0:function(){
        this.setOpacity(.7)
    },
    anim1:function(){
        this.setLeftOffset(this.state.leftDest);
    },
    anim2:function(){
        this.setTopOffset(this.state.topDest);
    },
    anim3:function(){
        this.setOpacity(.41)
    },
    render:function(){
        return

                     {this.state.copy}

    }

})

module.exports = MainPage;


text:
using System;
using System.Runtime.InteropServices;
using size_t = BrotliSharpLib.Brotli.SizeT;
using score_t = BrotliSharpLib.Brotli.SizeT;

namespace BrotliSharpLib {
    public static partial class Brotli {
        private class HashForgetfulChainH40 : Hasher {
            private const int BUCKET_BITS = 15;
            private const int NUM_LAST_DISTANCES_TO_CHECK = 1;
            private const int NUM_BANKS = 1;
            private const int BANK_BITS = 16;
            private const int BANK_SIZE = 1 << BANK_BITS;
            private const int BUCKET_SIZE = 1 << BUCKET_BITS;
            private const bool CAPPED_CHAINS = false;

            public override size_t HashTypeLength() {
                return 4;
            }

            public override size_t StoreLookahead() {
                return 4;
            }

            /* HashBytes is the function that chooses the bucket to place the address in. */
            private static unsafe uint HashBytes(byte* data) {
                uint h = *(uint*) (data) * kHashMul32;
                /* The higher bits contain more mixture from the multiplication,
                   so we take our results from there. */
                return h >> (32 - BUCKET_BITS);
            }

            [StructLayout(LayoutKind.Sequential)]
            private struct Slot {
                public ushort delta;
                public ushort next;
            }

            [StructLayout(LayoutKind.Sequential)]
            private unsafe struct Bank {
                private fixed ushort slots_[BANK_SIZE * 2];

                public Slot* slots(size_t index) {
                    fixed (ushort* s = slots_)
                        return (Slot*) &s[index * 2];
                }
            }

            [StructLayout(LayoutKind.Sequential)]
            private unsafe struct HashForgetfulChain {
                public fixed uint addr[BUCKET_SIZE];

                public fixed ushort head[BUCKET_SIZE];

                /* Truncated hash used for quick rejection of "distance cache" candidates. */
                public fixed byte tiny_hash[65536];

                private fixed ushort banks_[NUM_BANKS * (BANK_SIZE * 2)];
                public fixed ushort free_slot_idx[NUM_BANKS];
                public size_t max_hops;

                public Bank* banks(size_t index) {
                    fixed (ushort* s = banks_)
                        return (Bank*) &s[index * BANK_SIZE * 2];
                }
            }

            private static unsafe HashForgetfulChain* Self(HasherHandle handle) {
                return (HashForgetfulChain*) &GetHasherCommon(handle)[1];
            }

            public override unsafe void Initialize(HasherHandle handle, BrotliEncoderParams* params_) {
                Self(handle)->max_hops =
                    (params_->quality > 6 ? 7u : 8u) << (params_->quality - 4);
            }

            public override unsafe void Prepare(HasherHandle handle, bool one_shot, SizeT input_size, byte* data) {
                HashForgetfulChain* self = Self(handle);
                /* Partial preparation is 100 times slower (per socket). */
                size_t partial_prepare_threshold = BUCKET_SIZE >> 6;
                if (one_shot && input_size <= partial_prepare_threshold) {
                    size_t i;
                    for (i = 0; i < input_size; ++i) {
                        size_t bucket = HashBytes(&data[i]);
                        /* See InitEmpty comment. */
                        self->addr[bucket] = 0xCCCCCCCC;
                        self->head[bucket] = 0xCCCC;
                    }
                }
                else {
                    /* Fill |addr| array with 0xCCCCCCCC value. Because of wrapping, position
                       processed by hasher never reaches 3GB + 64M; this makes all new chains
                       to be terminated after the first node. */
                    memset(self->addr, 0xCC, BUCKET_SIZE * sizeof(uint));
                    memset(self->head, 0, BUCKET_SIZE * sizeof(ushort));
                }
                memset(self->tiny_hash, 0, 65536);
                memset(self->free_slot_idx, 0, NUM_BANKS * sizeof(ushort));
            }

            public override unsafe size_t HashMemAllocInBytes(BrotliEncoderParams* params_, bool one_shot,
                size_t input_size) {
                return sizeof(HashForgetfulChain);
            }

            /* Look at 4 bytes at &data[ix & mask]. Compute a hash from these, and prepend
               node to corresponding chain; also update tiny_hash for current position. */
            public override unsafe void Store(HasherHandle handle,
                byte* data, size_t mask, size_t ix) {
                HashForgetfulChain* self = Self(handle);
                size_t key = HashBytes(&data[ix & mask]);
                size_t bank = key & (NUM_BANKS - 1);
                size_t idx = self->free_slot_idx[bank]++ & (BANK_SIZE - 1);
                size_t delta = ix - self->addr[key];
                self->tiny_hash[(ushort) ix] = (byte) key;
                if (delta > 0xFFFF) delta = CAPPED_CHAINS ? 0 : 0xFFFF;
                self->banks(bank)->slots(idx)->delta = (ushort) delta;
                self->banks(bank)->slots(idx)->next = self->head[key];
                self->addr[key] = (uint) ix;
                self->head[key] = (ushort) idx;
            }

            public override unsafe void StoreRange(HasherHandle handle,
                byte* data, size_t mask, size_t ix_start,
                size_t ix_end) {
                size_t i;
                for (i = ix_start; i < ix_end; ++i) {
                    Store(handle, data, mask, i);
                }
            }

            public override unsafe void StitchToPreviousBlock(HasherHandle handle, size_t num_bytes, size_t position,
                byte* ringbuffer,
                size_t ringbuffer_mask) {
                if (num_bytes >= HashTypeLength() - 1 && position >= 3) {
                    /* Prepare the hashes for three last bytes of the last write.
                       These could not be calculated before, since they require knowledge
                       of both the previous and the current block. */
                    Store(handle, ringbuffer, ringbuffer_mask, position - 3);
                    Store(handle, ringbuffer, ringbuffer_mask, position - 2);
                    Store(handle, ringbuffer, ringbuffer_mask, position - 1);
                }
            }

            public override unsafe void PrepareDistanceCache(HasherHandle handle, int* distance_cache) {
                Brotli.PrepareDistanceCache(distance_cache, NUM_LAST_DISTANCES_TO_CHECK);
            }

            /* Find a longest backward match of &data[cur_ix] up to the length of
               max_length and stores the position cur_ix in the hash table.

               REQUIRES: FN(PrepareDistanceCache) must be invoked for current distance cache
                         values; if this method is invoked repeatedly with the same distance
                         cache values, it is enough to invoke FN(PrepareDistanceCache) once.

               Does not look for matches longer than max_length.
               Does not look for matches further away than max_backward.
               Writes the best match into |out|.
               Returns 1 when match is found, otherwise 0. */
            public override unsafe bool FindLongestMatch(HasherHandle handle,
                ushort* dictionary_hash,
                byte* data, size_t ring_buffer_mask,
                int* distance_cache,
                size_t cur_ix, size_t max_length, size_t max_backward,
                HasherSearchResult* out_) {
                HashForgetfulChain* self = Self(handle);
                size_t cur_ix_masked = cur_ix & ring_buffer_mask;
                bool is_match_found = false;
                /* Don't accept a short copy from far away. */
                score_t best_score = out_->score;
                size_t best_len = out_->len;
                size_t i;
                size_t key = HashBytes(&data[cur_ix_masked]);
                byte tiny_hash = (byte) (key);
                out_->len = 0;
                out_->len_x_code = 0;
                /* Try last distance first. */
                for (i = 0; i < NUM_LAST_DISTANCES_TO_CHECK; ++i) {
                    size_t backward = (size_t) distance_cache[i];
                    size_t prev_ix = (cur_ix - backward);
                    /* For distance code 0 we want to consider 2-byte matches. */
                    if (i > 0 && self->tiny_hash[(ushort) prev_ix] != tiny_hash) continue;
                    if (prev_ix >= cur_ix || backward > max_backward) {
                        continue;
                    }
                    prev_ix &= ring_buffer_mask;
                    {
                        size_t len = FindMatchLengthWithLimit(&data[prev_ix],
                            &data[cur_ix_masked],
                            max_length);
                        if (len >= 2) {
                            score_t score = BackwardReferenceScoreUsingLastDistance(len);
                            if (best_score < score) {
                                if (i != 0) score -= BackwardReferencePenaltyUsingLastDistance(i);
                                if (best_score < score) {
                                    best_score = score;
                                    best_len = len;
                                    out_->len = best_len;
                                    out_->distance = backward;
                                    out_->score = best_score;
                                    is_match_found = true;
                                }
                            }
                        }
                    }
                }
                {
                    size_t bank = key & (NUM_BANKS - 1);
                    size_t backward = 0;
                    size_t hops = self->max_hops;
                    size_t delta = cur_ix - self->addr[key];
                    size_t slot = self->head[key];
                    while ((hops--) != 0) {
                        size_t prev_ix;
                        size_t last = slot;
                        backward += delta;
                        if (backward > max_backward || (CAPPED_CHAINS && delta == 0)) break;
                        prev_ix = (cur_ix - backward) & ring_buffer_mask;
                        slot = self->banks(bank)->slots(last)->next;
                        delta = self->banks(bank)->slots(last)->delta;
                        if (cur_ix_masked + best_len > ring_buffer_mask ||
                            prev_ix + best_len > ring_buffer_mask ||
                            data[cur_ix_masked + best_len] != data[prev_ix + best_len]) {
                            continue;
                        }
                        {
                            size_t len = FindMatchLengthWithLimit(&data[prev_ix],
                                &data[cur_ix_masked],
                                max_length);
                            if (len >= 4) {
                                /* Comparing for >= 3 does not change the semantics, but just saves
                                   for a few unnecessary binary logarithms in backward reference
                                   score, since we are not interested in such short matches. */
                                score_t score = BackwardReferenceScore(len, backward);
                                if (best_score < score) {
                                    best_score = score;
                                    best_len = len;
                                    out_->len = best_len;
                                    out_->distance = backward;
                                    out_->score = best_score;
                                    is_match_found = true;
                                }
                            }
                        }
                    }
                    Store(handle, data, ring_buffer_mask, cur_ix);
                }
                if (!is_match_found) {
                    is_match_found = SearchInStaticDictionary(dictionary_hash,
                        handle, &data[cur_ix_masked], max_length, max_backward, out_,
                        false);
                }
                return is_match_found;
            }

            public override unsafe void CreateBackwardReferences(
                ushort* dictionary_hash,
                size_t num_bytes, size_t position,
                byte* ringbuffer, size_t ringbuffer_mask,
                BrotliEncoderParams* params_, HasherHandle hasher, int* dist_cache,
                size_t* last_insert_len, Command* commands, size_t* num_commands,
                size_t* num_literals) {
                /* Set maximum distance, see section 9.1. of the spec. */
                size_t max_backward_limit = BROTLI_MAX_BACKWARD_LIMIT(params_->lgwin);

                Command* orig_commands = commands;
                size_t insert_length = *last_insert_len;
                size_t pos_end = position + num_bytes;
                size_t store_end = num_bytes >= StoreLookahead()
                    ? position + num_bytes - StoreLookahead() + 1
                    : position;

                /* For speed up heuristics for random data. */
                size_t random_heuristics_window_size =
                    LiteralSpreeLengthForSparseSearch(params_);
                size_t apply_random_heuristics = position + random_heuristics_window_size;

                /* Minimum score to accept a backward reference. */
                score_t kMinScore = BROTLI_SCORE_BASE + 100;

                PrepareDistanceCache(hasher, dist_cache);

                while (position + HashTypeLength() < pos_end) {
                    size_t max_length = pos_end - position;
                    size_t max_distance = Math.Min(position, max_backward_limit);
                    HasherSearchResult sr = new HasherSearchResult();
                    sr.len = 0;
                    sr.len_x_code = 0;
                    sr.distance = 0;
                    sr.score = kMinScore;
                    if (FindLongestMatch(hasher, dictionary_hash,
                        ringbuffer, ringbuffer_mask, dist_cache,
                        position, max_length, max_distance, &sr)) {
                        /* Found a match. Let's look for something even better ahead. */
                        int delayed_backward_references_in_row = 0;
                        --max_length;
                        for (;; --max_length) {
                            score_t cost_diff_lazy = 175;
                            bool is_match_found;
                            HasherSearchResult sr2;
                            sr2.len = params_->quality < MIN_QUALITY_FOR_EXTENSIVE_REFERENCE_SEARCH
                                ? Math.Min(sr.len - 1, max_length)
                                : 0;
                            sr2.len_x_code = 0;
                            sr2.distance = 0;
                            sr2.score = kMinScore;
                            max_distance = Math.Min(position + 1, max_backward_limit);
                            is_match_found = FindLongestMatch(hasher,
                                dictionary_hash, ringbuffer, ringbuffer_mask, dist_cache,
                                position + 1, max_length, max_distance, &sr2);
                            if (is_match_found && sr2.score >= sr.score + cost_diff_lazy) {
                                /* Ok, let's just write one byte for now and start a match from the
                                   next byte. */
                                ++position;
                                ++insert_length;
                                sr = sr2;
                                if (++delayed_backward_references_in_row < 4 &&
                                    position + HashTypeLength() < pos_end) {
                                    continue;
                                }
                            }
                            break;
                        }
                        apply_random_heuristics =
                            position + 2 * sr.len + random_heuristics_window_size;
                        max_distance = Math.Min(position, max_backward_limit);
                        {
                            /* The first 16 codes are special short-codes,
                               and the minimum offset is 1. */
                            size_t distance_code =
                                ComputeDistanceCode(sr.distance, max_distance, dist_cache);
                            if (sr.distance <= max_distance && distance_code > 0) {
                                dist_cache[3] = dist_cache[2];
                                dist_cache[2] = dist_cache[1];
                                dist_cache[1] = dist_cache[0];
                                dist_cache[0] = (int) sr.distance;
                                PrepareDistanceCache(hasher, dist_cache);
                            }
                            InitCommand(commands++, insert_length, sr.len, sr.len ^ sr.len_x_code,
                                distance_code);
                        }
                        *num_literals += insert_length;
                        insert_length = 0;
                        /* Put the hash keys into the table, if there are enough bytes left.
                           Depending on the hasher implementation, it can push all positions
                           in the given range or only a subset of them. */
                        StoreRange(hasher, ringbuffer, ringbuffer_mask, position + 2,
                            Math.Min(position + sr.len, store_end));
                        position += sr.len;
                    }
                    else {
                        ++insert_length;
                        ++position;
                        /* If we have not seen matches for a long time, we can skip some
                           match lookups. Unsuccessful match lookups are very very expensive
                           and this kind of a heuristic speeds up compression quite
                           a lot. */
                        if (position > apply_random_heuristics) {
                            /* Going through uncompressible data, jump. */
                            if (position >
                                apply_random_heuristics + 4 * random_heuristics_window_size) {
                                /* It is quite a long time since we saw a copy, so we assume
                                   that this data is not compressible, and store hashes less
                                   often. Hashes of non compressible data are less likely to
                                   turn out to be useful in the future, too, so we store less of
                                   them to not to flood out the hash table of good compressible
                                   data. */
                                size_t kMargin =
                                    Math.Max(StoreLookahead() - 1, 4);
                                size_t pos_jump =
                                    Math.Min(position + 16, pos_end - kMargin);
                                for (; position < pos_jump; position += 4) {
                                    Store(hasher, ringbuffer, ringbuffer_mask, position);
                                    insert_length += 4;
                                }
                            }
                            else {
                                size_t kMargin =
                                    Math.Max(StoreLookahead() - 1, 2);
                                size_t pos_jump =
                                    Math.Min(position + 8, pos_end - kMargin);
                                for (; position < pos_jump; position += 2) {
                                    Store(hasher, ringbuffer, ringbuffer_mask, position);
                                    insert_length += 2;
                                }
                            }
                        }
                    }
                }
                insert_length += pos_end - position;
                *last_insert_len = insert_length;
                *num_commands += (size_t) (commands - orig_commands);
            }
        }

        private class HashForgetfulChainH41 : Hasher {
            private const int BUCKET_BITS = 15;
            private const int NUM_LAST_DISTANCES_TO_CHECK = 10;
            private const int NUM_BANKS = 1;
            private const int BANK_BITS = 16;
            private const int BANK_SIZE = 1 << BANK_BITS;
            private const int BUCKET_SIZE = 1 << BUCKET_BITS;
            private const bool CAPPED_CHAINS = false;

            public override size_t HashTypeLength() {
                return 4;
            }

            public override size_t StoreLookahead() {
                return 4;
            }

            /* HashBytes is the function that chooses the bucket to place the address in. */
            private static unsafe uint HashBytes(byte* data) {
                uint h = *(uint*) (data) * kHashMul32;
                /* The higher bits contain more mixture from the multiplication,
                   so we take our results from there. */
                return h >> (32 - BUCKET_BITS);
            }

            [StructLayout(LayoutKind.Sequential)]
            private struct Slot {
                public ushort delta;
                public ushort next;
            }

            [StructLayout(LayoutKind.Sequential)]
            private unsafe struct Bank {
                private fixed ushort slots_[BANK_SIZE * 2];

                public Slot* slots(size_t index) {
                    fixed (ushort* s = slots_)
                        return (Slot*) &s[index * 2];
                }
            }

            [StructLayout(LayoutKind.Sequential)]
            private unsafe struct HashForgetfulChain {
                public fixed uint addr[BUCKET_SIZE];

                public fixed ushort head[BUCKET_SIZE];

                /* Truncated hash used for quick rejection of "distance cache" candidates. */
                public fixed byte tiny_hash[65536];

                private fixed ushort banks_[NUM_BANKS * (BANK_SIZE * 2)];
                public fixed ushort free_slot_idx[NUM_BANKS];
                public size_t max_hops;

                public Bank* banks(size_t index) {
                    fixed (ushort* s = banks_)
                        return (Bank*) &s[index * BANK_SIZE * 2];
                }
            }

            private static unsafe HashForgetfulChain* Self(HasherHandle handle) {
                return (HashForgetfulChain*) &(GetHasherCommon(handle)[1]);
            }

            public override unsafe void Initialize(HasherHandle handle, BrotliEncoderParams* params_) {
                Self(handle)->max_hops =
                    (params_->quality > 6 ? 7u : 8u) << (params_->quality - 4);
            }

            public override unsafe void Prepare(HasherHandle handle, bool one_shot, SizeT input_size, byte* data) {
                HashForgetfulChain* self = Self(handle);
                /* Partial preparation is 100 times slower (per socket). */
                size_t partial_prepare_threshold = BUCKET_SIZE >> 6;
                if (one_shot && input_size <= partial_prepare_threshold) {
                    size_t i;
                    for (i = 0; i < input_size; ++i) {
                        size_t bucket = HashBytes(&data[i]);
                        /* See InitEmpty comment. */
                        self->addr[bucket] = 0xCCCCCCCC;
                        self->head[bucket] = 0xCCCC;
                    }
                }
                else {
                    /* Fill |addr| array with 0xCCCCCCCC value. Because of wrapping, position
                       processed by hasher never reaches 3GB + 64M; this makes all new chains
                       to be terminated after the first node. */
                    memset(self->addr, 0xCC, BUCKET_SIZE * sizeof(uint));
                    memset(self->head, 0, BUCKET_SIZE * sizeof(ushort));
                }
                memset(self->tiny_hash, 0, 65536);
                memset(self->free_slot_idx, 0, NUM_BANKS * sizeof(ushort));
            }

            public override unsafe size_t HashMemAllocInBytes(BrotliEncoderParams* params_, bool one_shot,
                size_t input_size) {
                return sizeof(HashForgetfulChain);
            }

            /* Look at 4 bytes at &data[ix & mask]. Compute a hash from these, and prepend
               node to corresponding chain; also update tiny_hash for current position. */
            public override unsafe void Store(HasherHandle handle,
                byte* data, size_t mask, size_t ix) {
                HashForgetfulChain* self = Self(handle);
                size_t key = HashBytes(&data[ix & mask]);
                size_t bank = key & (NUM_BANKS - 1);
                size_t idx = self->free_slot_idx[bank]++ & (BANK_SIZE - 1);
                size_t delta = ix - self->addr[key];
                self->tiny_hash[(ushort) ix] = (byte) key;
                if (delta > 0xFFFF) delta = CAPPED_CHAINS ? 0 : 0xFFFF;
                self->banks(bank)->slots(idx)->delta = (ushort) delta;
                self->banks(bank)->slots(idx)->next = self->head[key];
                self->addr[key] = (uint) ix;
                self->head[key] = (ushort) idx;
            }

            public override unsafe void StoreRange(HasherHandle handle,
                byte* data, size_t mask, size_t ix_start,
                size_t ix_end) {
                size_t i;
                for (i = ix_start; i < ix_end; ++i) {
                    Store(handle, data, mask, i);
                }
            }

            public override unsafe void StitchToPreviousBlock(HasherHandle handle, size_t num_bytes, size_t position,
                byte* ringbuffer,
                size_t ringbuffer_mask) {
                if (num_bytes >= HashTypeLength() - 1 && position >= 3) {
                    /* Prepare the hashes for three last bytes of the last write.
                       These could not be calculated before, since they require knowledge
                       of both the previous and the current block. */
                    Store(handle, ringbuffer, ringbuffer_mask, position - 3);
                    Store(handle, ringbuffer, ringbuffer_mask, position - 2);
                    Store(handle, ringbuffer, ringbuffer_mask, position - 1);
                }
            }

            public override unsafe void PrepareDistanceCache(HasherHandle handle, int* distance_cache) {
                Brotli.PrepareDistanceCache(distance_cache, NUM_LAST_DISTANCES_TO_CHECK);
            }

            /* Find a longest backward match of &data[cur_ix] up to the length of
               max_length and stores the position cur_ix in the hash table.

               REQUIRES: FN(PrepareDistanceCache) must be invoked for current distance cache
                         values; if this method is invoked repeatedly with the same distance
                         cache values, it is enough to invoke FN(PrepareDistanceCache) once.

               Does not look for matches longer than max_length.
               Does not look for matches further away than max_backward.
               Writes the best match into |out|.
               Returns 1 when match is found, otherwise 0. */
            public override unsafe bool FindLongestMatch(HasherHandle handle,
                ushort* dictionary_hash,
                byte* data, size_t ring_buffer_mask,
                int* distance_cache,
                size_t cur_ix, size_t max_length, size_t max_backward,
                HasherSearchResult* out_) {
                HashForgetfulChain* self = Self(handle);
                size_t cur_ix_masked = cur_ix & ring_buffer_mask;
                bool is_match_found = false;
                /* Don't accept a short copy from far away. */
                score_t best_score = out_->score;
                size_t best_len = out_->len;
                size_t i;
                size_t key = HashBytes(&data[cur_ix_masked]);
                byte tiny_hash = (byte) (key);
                out_->len = 0;
                out_->len_x_code = 0;
                /* Try last distance first. */
                for (i = 0; i < NUM_LAST_DISTANCES_TO_CHECK; ++i) {
                    size_t backward = (size_t) distance_cache[i];
                    size_t prev_ix = (cur_ix - backward);
                    /* For distance code 0 we want to consider 2-byte matches. */
                    if (i > 0 && self->tiny_hash[(ushort) prev_ix] != tiny_hash) continue;
                    if (prev_ix >= cur_ix || backward > max_backward) {
                        continue;
                    }
                    prev_ix &= ring_buffer_mask;
                    {
                        size_t len = FindMatchLengthWithLimit(&data[prev_ix],
                            &data[cur_ix_masked],
                            max_length);
                        if (len >= 2) {
                            score_t score = BackwardReferenceScoreUsingLastDistance(len);
                            if (best_score < score) {
                                if (i != 0) score -= BackwardReferencePenaltyUsingLastDistance(i);
                                if (best_score < score) {
                                    best_score = score;
                                    best_len = len;
                                    out_->len = best_len;
                                    out_->distance = backward;
                                    out_->score = best_score;
                                    is_match_found = true;
                                }
                            }
                        }
                    }
                }
                {
                    size_t bank = key & (NUM_BANKS - 1);
                    size_t backward = 0;
                    size_t hops = self->max_hops;
                    size_t delta = cur_ix - self->addr[key];
                    size_t slot = self->head[key];
                    while ((hops--) != 0) {
                        size_t prev_ix;
                        size_t last = slot;
                        backward += delta;
                        if (backward > max_backward || (CAPPED_CHAINS && delta == 0)) break;
                        prev_ix = (cur_ix - backward) & ring_buffer_mask;
                        slot = self->banks(bank)->slots(last)->next;
                        delta = self->banks(bank)->slots(last)->delta;
                        if (cur_ix_masked + best_len > ring_buffer_mask ||
                            prev_ix + best_len > ring_buffer_mask ||
                            data[cur_ix_masked + best_len] != data[prev_ix + best_len]) {
                            continue;
                        }
                        {
                            size_t len = FindMatchLengthWithLimit(&data[prev_ix],
                                &data[cur_ix_masked],
                                max_length);
                            if (len >= 4) {
                                /* Comparing for >= 3 does not change the semantics, but just saves
                                   for a few unnecessary binary logarithms in backward reference
                                   score, since we are not interested in such short matches. */
                                score_t score = BackwardReferenceScore(len, backward);
                                if (best_score < score) {
                                    best_score = score;
                                    best_len = len;
                                    out_->len = best_len;
                                    out_->distance = backward;
                                    out_->score = best_score;
                                    is_match_found = true;
                                }
                            }
                        }
                    }
                    Store(handle, data, ring_buffer_mask, cur_ix);
                }
                if (!is_match_found) {
                    is_match_found = SearchInStaticDictionary(dictionary_hash,
                        handle, &data[cur_ix_masked], max_length, max_backward, out_,
                        false);
                }
                return is_match_found;
            }

            public override unsafe void CreateBackwardReferences(
                ushort* dictionary_hash,
                size_t num_bytes, size_t position,
                byte* ringbuffer, size_t ringbuffer_mask,
                BrotliEncoderParams* params_, HasherHandle hasher, int* dist_cache,
                size_t* last_insert_len, Command* commands, size_t* num_commands,
                size_t* num_literals) {
                /* Set maximum distance, see section 9.1. of the spec. */
                size_t max_backward_limit = BROTLI_MAX_BACKWARD_LIMIT(params_->lgwin);

                Command* orig_commands = commands;
                size_t insert_length = *last_insert_len;
                size_t pos_end = position + num_bytes;
                size_t store_end = num_bytes >= StoreLookahead()
                    ? position + num_bytes - StoreLookahead() + 1
                    : position;

                /* For speed up heuristics for random data. */
                size_t random_heuristics_window_size =
                    LiteralSpreeLengthForSparseSearch(params_);
                size_t apply_random_heuristics = position + random_heuristics_window_size;

                /* Minimum score to accept a backward reference. */
                score_t kMinScore = BROTLI_SCORE_BASE + 100;

                PrepareDistanceCache(hasher, dist_cache);

                while (position + HashTypeLength() < pos_end) {
                    size_t max_length = pos_end - position;
                    size_t max_distance = Math.Min(position, max_backward_limit);
                    HasherSearchResult sr = new HasherSearchResult();
                    sr.len = 0;
                    sr.len_x_code = 0;
                    sr.distance = 0;
                    sr.score = kMinScore;
                    if (FindLongestMatch(hasher, dictionary_hash,
                        ringbuffer, ringbuffer_mask, dist_cache,
                        position, max_length, max_distance, &sr)) {
                        /* Found a match. Let's look for something even better ahead. */
                        int delayed_backward_references_in_row = 0;
                        --max_length;
                        for (;; --max_length) {
                            score_t cost_diff_lazy = 175;
                            bool is_match_found;
                            HasherSearchResult sr2;
                            sr2.len = params_->quality < MIN_QUALITY_FOR_EXTENSIVE_REFERENCE_SEARCH
                                ? Math.Min(sr.len - 1, max_length)
                                : 0;
                            sr2.len_x_code = 0;
                            sr2.distance = 0;
                            sr2.score = kMinScore;
                            max_distance = Math.Min(position + 1, max_backward_limit);
                            is_match_found = FindLongestMatch(hasher,
                                dictionary_hash, ringbuffer, ringbuffer_mask, dist_cache,
                                position + 1, max_length, max_distance, &sr2);
                            if (is_match_found && sr2.score >= sr.score + cost_diff_lazy) {
                                /* Ok, let's just write one byte for now and start a match from the
                                   next byte. */
                                ++position;
                                ++insert_length;
                                sr = sr2;
                                if (++delayed_backward_references_in_row < 4 &&
                                    position + HashTypeLength() < pos_end) {
                                    continue;
                                }
                            }
                            break;
                        }
                        apply_random_heuristics =
                            position + 2 * sr.len + random_heuristics_window_size;
                        max_distance = Math.Min(position, max_backward_limit);
                        {
                            /* The first 16 codes are special short-codes,
                               and the minimum offset is 1. */
                            size_t distance_code =
                                ComputeDistanceCode(sr.distance, max_distance, dist_cache);
                            if (sr.distance <= max_distance && distance_code > 0) {
                                dist_cache[3] = dist_cache[2];
                                dist_cache[2] = dist_cache[1];
                                dist_cache[1] = dist_cache[0];
                                dist_cache[0] = (int) sr.distance;
                                PrepareDistanceCache(hasher, dist_cache);
                            }
                            InitCommand(commands++, insert_length, sr.len, sr.len ^ sr.len_x_code,
                                distance_code);
                        }
                        *num_literals += insert_length;
                        insert_length = 0;
                        /* Put the hash keys into the table, if there are enough bytes left.
                           Depending on the hasher implementation, it can push all positions
                           in the given range or only a subset of them. */
                        StoreRange(hasher, ringbuffer, ringbuffer_mask, position + 2,
                            Math.Min(position + sr.len, store_end));
                        position += sr.len;
                    }
                    else {
                        ++insert_length;
                        ++position;
                        /* If we have not seen matches for a long time, we can skip some
                           match lookups. Unsuccessful match lookups are very very expensive
                           and this kind of a heuristic speeds up compression quite
                           a lot. */
                        if (position > apply_random_heuristics) {
                            /* Going through uncompressible data, jump. */
                            if (position >
                                apply_random_heuristics + 4 * random_heuristics_window_size) {
                                /* It is quite a long time since we saw a copy, so we assume
                                   that this data is not compressible, and store hashes less
                                   often. Hashes of non compressible data are less likely to
                                   turn out to be useful in the future, too, so we store less of
                                   them to not to flood out the hash table of good compressible
                                   data. */
                                size_t kMargin =
                                    Math.Max(StoreLookahead() - 1, 4);
                                size_t pos_jump =
                                    Math.Min(position + 16, pos_end - kMargin);
                                for (; position < pos_jump; position += 4) {
                                    Store(hasher, ringbuffer, ringbuffer_mask, position);
                                    insert_length += 4;
                                }
                            }
                            else {
                                size_t kMargin =
                                    Math.Max(StoreLookahead() - 1, 2);
                                size_t pos_jump =
                                    Math.Min(position + 8, pos_end - kMargin);
                                for (; position < pos_jump; position += 2) {
                                    Store(hasher, ringbuffer, ringbuffer_mask, position);
                                    insert_length += 2;
                                }
                            }
                        }
                    }
                }
                insert_length += pos_end - position;
                *last_insert_len = insert_length;
                *num_commands += (size_t) (commands - orig_commands);
            }
        }

        private class HashForgetfulChainH42 : Hasher {
            private const int BUCKET_BITS = 15;
            private const int NUM_LAST_DISTANCES_TO_CHECK = 16;
            private const int NUM_BANKS = 512;
            private const int BANK_BITS = 9;
            private const int BANK_SIZE = 1 << BANK_BITS;
            private const int BUCKET_SIZE = 1 << BUCKET_BITS;
            private const bool CAPPED_CHAINS = false;

            public override size_t HashTypeLength() {
                return 4;
            }

            public override size_t StoreLookahead() {
                return 4;
            }

            /* HashBytes is the function that chooses the bucket to place the address in. */
            private static unsafe uint HashBytes(byte* data) {
                uint h = *(uint*) (data) * kHashMul32;
                /* The higher bits contain more mixture from the multiplication,
                   so we take our results from there. */
                return h >> (32 - BUCKET_BITS);
            }

            [StructLayout(LayoutKind.Sequential)]
            private struct Slot {
                public ushort delta;
                public ushort next;
            }

            [StructLayout(LayoutKind.Sequential)]
            private unsafe struct Bank {
                private fixed ushort slots_[BANK_SIZE * 2];

                public Slot* slots(size_t index) {
                    fixed (ushort* s = slots_)
                        return (Slot*) &s[index * 2];
                }
            }

            [StructLayout(LayoutKind.Sequential)]
            private unsafe struct HashForgetfulChain {
                public fixed uint addr[BUCKET_SIZE];

                public fixed ushort head[BUCKET_SIZE];

                /* Truncated hash used for quick rejection of "distance cache" candidates. */
                public fixed byte tiny_hash[65536];

                private fixed ushort banks_[NUM_BANKS * (BANK_SIZE * 2)];
                public fixed ushort free_slot_idx[NUM_BANKS];
                public size_t max_hops;

                public Bank* banks(size_t index) {
                    fixed (ushort* s = banks_)
                        return (Bank*) &s[index * BANK_SIZE * 2];
                }
            }

            private static unsafe HashForgetfulChain* Self(HasherHandle handle) {
                return (HashForgetfulChain*) &(GetHasherCommon(handle)[1]);
            }

            public override unsafe void Initialize(HasherHandle handle, BrotliEncoderParams* params_) {
                Self(handle)->max_hops =
                    (params_->quality > 6 ? 7u : 8u) << (params_->quality - 4);
            }

            public override unsafe void Prepare(HasherHandle handle, bool one_shot, SizeT input_size, byte* data) {
                HashForgetfulChain* self = Self(handle);
                /* Partial preparation is 100 times slower (per socket). */
                size_t partial_prepare_threshold = BUCKET_SIZE >> 6;
                if (one_shot && input_size <= partial_prepare_threshold) {
                    size_t i;
                    for (i = 0; i < input_size; ++i) {
                        size_t bucket = HashBytes(&data[i]);
                        /* See InitEmpty comment. */
                        self->addr[bucket] = 0xCCCCCCCC;
                        self->head[bucket] = 0xCCCC;
                    }
                }
                else {
                    /* Fill |addr| array with 0xCCCCCCCC value. Because of wrapping, position
                       processed by hasher never reaches 3GB + 64M; this makes all new chains
                       to be terminated after the first node. */
                    memset(self->addr, 0xCC, BUCKET_SIZE * sizeof(uint));
                    memset(self->head, 0, BUCKET_SIZE * sizeof(ushort));
                }
                memset(self->tiny_hash, 0, 65536);
                memset(self->free_slot_idx, 0, NUM_BANKS * sizeof(ushort));
            }

            public override unsafe size_t HashMemAllocInBytes(BrotliEncoderParams* params_, bool one_shot,
                size_t input_size) {
                return sizeof(HashForgetfulChain);
            }

            /* Look at 4 bytes at &data[ix & mask]. Compute a hash from these, and prepend
               node to corresponding chain; also update tiny_hash for current position. */
            public override unsafe void Store(HasherHandle handle,
                byte* data, size_t mask, size_t ix) {
                HashForgetfulChain* self = Self(handle);
                size_t key = HashBytes(&data[ix & mask]);
                size_t bank = key & (NUM_BANKS - 1);
                size_t idx = self->free_slot_idx[bank]++ & (BANK_SIZE - 1);
                size_t delta = ix - self->addr[key];
                self->tiny_hash[(ushort) ix] = (byte) key;
                if (delta > 0xFFFF) delta = CAPPED_CHAINS ? 0 : 0xFFFF;
                self->banks(bank)->slots(idx)->delta = (ushort) delta;
                self->banks(bank)->slots(idx)->next = self->head[key];
                self->addr[key] = (uint) ix;
                self->head[key] = (ushort) idx;
            }

            public override unsafe void StoreRange(HasherHandle handle,
                byte* data, size_t mask, size_t ix_start,
                size_t ix_end) {
                size_t i;
                for (i = ix_start; i < ix_end; ++i) {
                    Store(handle, data, mask, i);
                }
            }

            public override unsafe void StitchToPreviousBlock(HasherHandle handle, size_t num_bytes, size_t position,
                byte* ringbuffer,
                size_t ringbuffer_mask) {
                if (num_bytes >= HashTypeLength() - 1 && position >= 3) {
                    /* Prepare the hashes for three last bytes of the last write.
                       These could not be calculated before, since they require knowledge
                       of both the previous and the current block. */
                    Store(handle, ringbuffer, ringbuffer_mask, position - 3);
                    Store(handle, ringbuffer, ringbuffer_mask, position - 2);
                    Store(handle, ringbuffer, ringbuffer_mask, position - 1);
                }
            }

            public override unsafe void PrepareDistanceCache(HasherHandle handle, int* distance_cache) {
                Brotli.PrepareDistanceCache(distance_cache, NUM_LAST_DISTANCES_TO_CHECK);
            }

            /* Find a longest backward match of &data[cur_ix] up to the length of
               max_length and stores the position cur_ix in the hash table.

               REQUIRES: FN(PrepareDistanceCache) must be invoked for current distance cache
                         values; if this method is invoked repeatedly with the same distance
                         cache values, it is enough to invoke FN(PrepareDistanceCache) once.

               Does not look for matches longer than max_length.
               Does not look for matches further away than max_backward.
               Writes the best match into |out|.
               Returns 1 when match is found, otherwise 0. */
            public override unsafe bool FindLongestMatch(HasherHandle handle,
                ushort* dictionary_hash,
                byte* data, size_t ring_buffer_mask,
                int* distance_cache,
                size_t cur_ix, size_t max_length, size_t max_backward,
                HasherSearchResult* out_) {
                HashForgetfulChain* self = Self(handle);
                size_t cur_ix_masked = cur_ix & ring_buffer_mask;
                bool is_match_found = false;
                /* Don't accept a short copy from far away. */
                score_t best_score = out_->score;
                size_t best_len = out_->len;
                size_t i;
                size_t key = HashBytes(&data[cur_ix_masked]);
                byte tiny_hash = (byte) (key);
                out_->len = 0;
                out_->len_x_code = 0;
                /* Try last distance first. */
                for (i = 0; i < NUM_LAST_DISTANCES_TO_CHECK; ++i) {
                    size_t backward = (size_t) distance_cache[i];
                    size_t prev_ix = (cur_ix - backward);
                    /* For distance code 0 we want to consider 2-byte matches. */
                    if (i > 0 && self->tiny_hash[(ushort) prev_ix] != tiny_hash) continue;
                    if (prev_ix >= cur_ix || backward > max_backward) {
                        continue;
                    }
                    prev_ix &= ring_buffer_mask;
                    {
                        size_t len = FindMatchLengthWithLimit(&data[prev_ix],
                            &data[cur_ix_masked],
                            max_length);
                        if (len >= 2) {
                            score_t score = BackwardReferenceScoreUsingLastDistance(len);
                            if (best_score < score) {
                                if (i != 0) score -= BackwardReferencePenaltyUsingLastDistance(i);
                                if (best_score < score) {
                                    best_score = score;
                                    best_len = len;
                                    out_->len = best_len;
                                    out_->distance = backward;
                                    out_->score = best_score;
                                    is_match_found = true;
                                }
                            }
                        }
                    }
                }
                {
                    size_t bank = key & (NUM_BANKS - 1);
                    size_t backward = 0;
                    size_t hops = self->max_hops;
                    size_t delta = cur_ix - self->addr[key];
                    size_t slot = self->head[key];
                    while ((hops--) != 0) {
                        size_t prev_ix;
                        size_t last = slot;
                        backward += delta;
                        if (backward > max_backward || (CAPPED_CHAINS && delta == 0)) break;
                        prev_ix = (cur_ix - backward) & ring_buffer_mask;
                        slot = self->banks(bank)->slots(last)->next;
                        delta = self->banks(bank)->slots(last)->delta;
                        if (cur_ix_masked + best_len > ring_buffer_mask ||
                            prev_ix + best_len > ring_buffer_mask ||
                            data[cur_ix_masked + best_len] != data[prev_ix + best_len]) {
                            continue;
                        }
                        {
                            size_t len = FindMatchLengthWithLimit(&data[prev_ix],
                                &data[cur_ix_masked],
                                max_length);
                            if (len >= 4) {
                                /* Comparing for >= 3 does not change the semantics, but just saves
                                   for a few unnecessary binary logarithms in backward reference
                                   score, since we are not interested in such short matches. */
                                score_t score = BackwardReferenceScore(len, backward);
                                if (best_score < score) {
                                    best_score = score;
                                    best_len = len;
                                    out_->len = best_len;
                                    out_->distance = backward;
                                    out_->score = best_score;
                                    is_match_found = true;
                                }
                            }
                        }
                    }
                    Store(handle, data, ring_buffer_mask, cur_ix);
                }
                if (!is_match_found) {
                    is_match_found = SearchInStaticDictionary(dictionary_hash,
                        handle, &data[cur_ix_masked], max_length, max_backward, out_,
                        false);
                }
                return is_match_found;
            }

            public override unsafe void CreateBackwardReferences(
                ushort* dictionary_hash,
                size_t num_bytes, size_t position,
                byte* ringbuffer, size_t ringbuffer_mask,
                BrotliEncoderParams* params_, HasherHandle hasher, int* dist_cache,
                size_t* last_insert_len, Command* commands, size_t* num_commands,
                size_t* num_literals) {
                /* Set maximum distance, see section 9.1. of the spec. */
                size_t max_backward_limit = BROTLI_MAX_BACKWARD_LIMIT(params_->lgwin);

                Command* orig_commands = commands;
                size_t insert_length = *last_insert_len;
                size_t pos_end = position + num_bytes;
                size_t store_end = num_bytes >= StoreLookahead()
                    ? position + num_bytes - StoreLookahead() + 1
                    : position;

                /* For speed up heuristics for random data. */
                size_t random_heuristics_window_size =
                    LiteralSpreeLengthForSparseSearch(params_);
                size_t apply_random_heuristics = position + random_heuristics_window_size;

                /* Minimum score to accept a backward reference. */
                score_t kMinScore = BROTLI_SCORE_BASE + 100;

                PrepareDistanceCache(hasher, dist_cache);

                while (position + HashTypeLength() < pos_end) {
                    size_t max_length = pos_end - position;
                    size_t max_distance = Math.Min(position, max_backward_limit);
                    HasherSearchResult sr = new HasherSearchResult();
                    sr.len = 0;
                    sr.len_x_code = 0;
                    sr.distance = 0;
                    sr.score = kMinScore;
                    if (FindLongestMatch(hasher, dictionary_hash,
                        ringbuffer, ringbuffer_mask, dist_cache,
                        position, max_length, max_distance, &sr)) {
                        /* Found a match. Let's look for something even better ahead. */
                        int delayed_backward_references_in_row = 0;
                        --max_length;
                        for (;; --max_length) {
                            score_t cost_diff_lazy = 175;
                            bool is_match_found;
                            HasherSearchResult sr2;
                            sr2.len = params_->quality < MIN_QUALITY_FOR_EXTENSIVE_REFERENCE_SEARCH
                                ? Math.Min(sr.len - 1, max_length)
                                : 0;
                            sr2.len_x_code = 0;
                            sr2.distance = 0;
                            sr2.score = kMinScore;
                            max_distance = Math.Min(position + 1, max_backward_limit);
                            is_match_found = FindLongestMatch(hasher,
                                dictionary_hash, ringbuffer, ringbuffer_mask, dist_cache,
                                position + 1, max_length, max_distance, &sr2);
                            if (is_match_found && sr2.score >= sr.score + cost_diff_lazy) {
                                /* Ok, let's just write one byte for now and start a match from the
                                   next byte. */
                                ++position;
                                ++insert_length;
                                sr = sr2;
                                if (++delayed_backward_references_in_row < 4 &&
                                    position + HashTypeLength() < pos_end) {
                                    continue;
                                }
                            }
                            break;
                        }
                        apply_random_heuristics =
                            position + 2 * sr.len + random_heuristics_window_size;
                        max_distance = Math.Min(position, max_backward_limit);
                        {
                            /* The first 16 codes are special short-codes,
                               and the minimum offset is 1. */
                            size_t distance_code =
                                ComputeDistanceCode(sr.distance, max_distance, dist_cache);
                            if (sr.distance <= max_distance && distance_code > 0) {
                                dist_cache[3] = dist_cache[2];
                                dist_cache[2] = dist_cache[1];
                                dist_cache[1] = dist_cache[0];
                                dist_cache[0] = (int) sr.distance;
                                PrepareDistanceCache(hasher, dist_cache);
                            }
                            InitCommand(commands++, insert_length, sr.len, sr.len ^ sr.len_x_code,
                                distance_code);
                        }
                        *num_literals += insert_length;
                        insert_length = 0;
                        /* Put the hash keys into the table, if there are enough bytes left.
                           Depending on the hasher implementation, it can push all positions
                           in the given range or only a subset of them. */
                        StoreRange(hasher, ringbuffer, ringbuffer_mask, position + 2,
                            Math.Min(position + sr.len, store_end));
                        position += sr.len;
                    }
                    else {
                        ++insert_length;
                        ++position;
                        /* If we have not seen matches for a long time, we can skip some
                           match lookups. Unsuccessful match lookups are very very expensive
                           and this kind of a heuristic speeds up compression quite
                           a lot. */
                        if (position > apply_random_heuristics) {
                            /* Going through uncompressible data, jump. */
                            if (position >
                                apply_random_heuristics + 4 * random_heuristics_window_size) {
                                /* It is quite a long time since we saw a copy, so we assume
                                   that this data is not compressible, and store hashes less
                                   often. Hashes of non compressible data are less likely to
                                   turn out to be useful in the future, too, so we store less of
                                   them to not to flood out the hash table of good compressible
                                   data. */
                                size_t kMargin =
                                    Math.Max(StoreLookahead() - 1, 4);
                                size_t pos_jump =
                                    Math.Min(position + 16, pos_end - kMargin);
                                for (; position < pos_jump; position += 4) {
                                    Store(hasher, ringbuffer, ringbuffer_mask, position);
                                    insert_length += 4;
                                }
                            }
                            else {
                                size_t kMargin =
                                    Math.Max(StoreLookahead() - 1, 2);
                                size_t pos_jump =
                                    Math.Min(position + 8, pos_end - kMargin);
                                for (; position < pos_jump; position += 2) {
                                    Store(hasher, ringbuffer, ringbuffer_mask, position);
                                    insert_length += 2;
                                }
                            }
                        }
                    }
                }
                insert_length += pos_end - position;
                *last_insert_len = insert_length;
                *num_commands += (size_t) (commands - orig_commands);
            }
        }
    }
}