text:
Originally published: 2017-04-03 13:19:41
Last updated: 2017-04-03 13:37:34
Author: Miguel Martínez López

Idle is installed by default on windows.

For Ubuntu, Linux Mint and Debian run:

      sudo apt-get install idle-python2.7

A tree structure is drawn on a Tkinter Canvas object. A tree item is an object with an icon and a text. The item maybe be expandable and/or editable. A tree item has two kind of icons: A normal icon and an icon when the item is selected. To create the tree structure, it's necessary to create a link between tree items, using a parent-child relationship.

The canvas is built using a *idlelib.TreeWidget.ScrolledCanvas* class. The *frame* attribute of this object contains a *Frame* Tkinter widget prepared for scrolling. This frame allocates a Tkinter *Canvas* and Tkinter *Scrollbars*. This is the signature:

       ScrolledCanvas(master, **options_for_the_tkinter_canvas)

It accepts exactly the same arguments than a *Canvas* widget.

A tree item should be a subclass of *idlelib.TreeWidget.TreeItem*.

The parent-child relationship between tree items is established using the *idlelib.TreeWidget.TreeNode* class.

This is the signature for **TreeNode(canvas, parent, item)**:

- *canvas* should be a *ScrolledCanvas* instance.
- *parent* should be the parent item. Leave that to *None* to create a root node.
- *item* should be the child item

*FileTreeItem* is a type of *TreeItem*. The only argument of a file tree item is a path.

Here there is an example of a custom tree item:

https://code.activestate.com/recipes/579077-bookmarks-browser-for-firefox/

To create your own tree items, it's required to subclass *TreeItem*. These are the methods that should be overrided:

- *GetText:* It should return the text string to display.
- *GetLabelText:* It should return label text string to display in front of text (Optional).
- *IsExpandable:* It should return a boolean indicating whether the istem is expandable
- *IsEditable:* It should return a boolean indicating whether the item's text may be edited.
- *SetText:* Get the text to change if the item is is editable
- *GetIconName:* Return name of icon to be displayed normally (Icons should be included in *ICONDIR* directory)
- *GetSelectedIconName:*  Return name of icon to be displayed when selected (Icons should be included in *ICONDIR* directory).
- *GetSubList:* It should return a list of child items (Optional. If not defined, the element is not expandable)
- *OnDoubleClick:* Called on a double-click on the item. (Optional)

Icons should be included in "Icons" subdirectory of path to idlelib library. If you want to use other path, just change *ICONDIR* variable to path to your icons:

     import idlelib
     idlelib.ICONDIR = "Your path to your icons"

Run this to find the path to *idlelib* module:

     python -c "import idlelib; print(idlelib.__file__)"


text:
"""Test configuration for the ZHA component."""
import itertools
import time
from unittest.mock import AsyncMock, MagicMock, PropertyMock, patch

import pytest
import zigpy
from zigpy.application import ControllerApplication
import zigpy.config
from zigpy.const import SIG_EP_INPUT, SIG_EP_OUTPUT, SIG_EP_PROFILE, SIG_EP_TYPE
import zigpy.device
import zigpy.group
import zigpy.profiles
from zigpy.state import State
import zigpy.types
import zigpy.zdo.types as zdo_t

from homeassistant.components.zha import DOMAIN
import homeassistant.components.zha.core.const as zha_const
import homeassistant.components.zha.core.device as zha_core_device
from homeassistant.setup import async_setup_component

from tests.common import MockConfigEntry
from tests.components.light.conftest import mock_light_profiles  # noqa: F401
from tests.components.zha import common

FIXTURE_GRP_ID = 0x1001
FIXTURE_GRP_NAME = "fixture group"

@pytest.fixture(scope="session", autouse=True)
def globally_load_quirks():
    """Load quirks automatically so that ZHA tests run deterministically in isolation.

    If portions of the ZHA test suite that do not happen to load quirks are run
    independently, bugs can emerge that will show up only when more of the test suite is
    run.
    """

    import zhaquirks

    zhaquirks.setup()

@pytest.fixture
def zigpy_app_controller():
    """Zigpy ApplicationController fixture."""
    app = MagicMock(spec_set=ControllerApplication)
    app.startup = AsyncMock()
    app.shutdown = AsyncMock()
    groups = zigpy.group.Groups(app)
    groups.add_group(FIXTURE_GRP_ID, FIXTURE_GRP_NAME, suppress_event=True)
    app.configure_mock(groups=groups)
    type(app).ieee = PropertyMock()
    app.ieee.return_value = zigpy.types.EUI64.convert("00:15:8d:00:02:32:4f:32")
    type(app).nwk = PropertyMock(return_value=zigpy.types.NWK(0x0000))
    type(app).devices = PropertyMock(return_value={})
    type(app).state = PropertyMock(return_value=State())
    return app

@pytest.fixture(name="config_entry")
async def config_entry_fixture(hass):
    """Fixture representing a config entry."""
    entry = MockConfigEntry(
        version=2,
        domain=zha_const.DOMAIN,
        data={
            zigpy.config.CONF_DEVICE: {zigpy.config.CONF_DEVICE_PATH: "/dev/ttyUSB0"},
            zha_const.CONF_RADIO_TYPE: "ezsp",
        },
        options={
            zha_const.CUSTOM_CONFIGURATION: {
                zha_const.ZHA_ALARM_OPTIONS: {
                    zha_const.CONF_ALARM_ARM_REQUIRES_CODE: False,
                    zha_const.CONF_ALARM_MASTER_CODE: "4321",
                    zha_const.CONF_ALARM_FAILED_TRIES: 2,
                }
            }
        },
    )
    entry.add_to_hass(hass)
    return entry

@pytest.fixture
def setup_zha(hass, config_entry, zigpy_app_controller):
    """Set up ZHA component."""
    zha_config = {zha_const.CONF_ENABLE_QUIRKS: False}

    p1 = patch(
        "bellows.zigbee.application.ControllerApplication.new",
        return_value=zigpy_app_controller,
    )

    async def _setup(config=None):
        config = config or {}
        with p1:
            status = await async_setup_component(
                hass, zha_const.DOMAIN, {zha_const.DOMAIN: {**zha_config, **config}}
            )
            assert status is True
            await hass.async_block_till_done()

    return _setup

@pytest.fixture
def channel():
    """Channel mock factory fixture."""

    def channel(name: str, cluster_id: int, endpoint_id: int = 1):
        ch = MagicMock()
        ch.name = name
        ch.generic_id = f"channel_0x{cluster_id:04x}"
        ch.id = f"{endpoint_id}:0x{cluster_id:04x}"
        ch.async_configure = AsyncMock()
        ch.async_initialize = AsyncMock()
        return ch

    return channel

@pytest.fixture
def zigpy_device_mock(zigpy_app_controller):
    """Make a fake device using the specified cluster classes."""

    def _mock_dev(
        endpoints,
        ieee="00:0d:6f:00:0a:90:69:e7",
        manufacturer="FakeManufacturer",
        model="FakeModel",
        node_descriptor=b"\x02@\x807\x10\x7fd\x00\x00*d\x00\x00",
        nwk=0xB79C,
        patch_cluster=True,
        quirk=None,
    ):
        """Make a fake device using the specified cluster classes."""
        device = zigpy.device.Device(
            zigpy_app_controller, zigpy.types.EUI64.convert(ieee), nwk
        )
        device.manufacturer = manufacturer
        device.model = model
        device.node_desc = zdo_t.NodeDescriptor.deserialize(node_descriptor)[0]
        device.last_seen = time.time()

        for epid, ep in endpoints.items():
            endpoint = device.add_endpoint(epid)
            endpoint.device_type = ep[SIG_EP_TYPE]
            endpoint.profile_id = ep.get(SIG_EP_PROFILE)
            endpoint.request = AsyncMock(return_value=[0])

            for cluster_id in ep.get(SIG_EP_INPUT, []):
                endpoint.add_input_cluster(cluster_id)

            for cluster_id in ep.get(SIG_EP_OUTPUT, []):
                endpoint.add_output_cluster(cluster_id)

        if quirk:
            device = quirk(zigpy_app_controller, device.ieee, device.nwk, device)

        if patch_cluster:
            for endpoint in (ep for epid, ep in device.endpoints.items() if epid):
                endpoint.request = AsyncMock(return_value=[0])
                for cluster in itertools.chain(
                    endpoint.in_clusters.values(), endpoint.out_clusters.values()
                ):
                    common.patch_cluster(cluster)

        return device

    return _mock_dev

@pytest.fixture
def zha_device_joined(hass, setup_zha):
    """Return a newly joined ZHA device."""

    async def _zha_device(zigpy_dev):
        zigpy_dev.last_seen = time.time()
        await setup_zha()
        zha_gateway = common.get_zha_gateway(hass)
        await zha_gateway.async_device_initialized(zigpy_dev)
        await hass.async_block_till_done()
        return zha_gateway.get_device(zigpy_dev.ieee)

    return _zha_device

@pytest.fixture
def zha_device_restored(hass, zigpy_app_controller, setup_zha, hass_storage):
    """Return a restored ZHA device."""

    async def _zha_device(zigpy_dev, last_seen=None):
        zigpy_app_controller.devices[zigpy_dev.ieee] = zigpy_dev

        if last_seen is not None:
            hass_storage[f"{DOMAIN}.storage"] = {
                "key": f"{DOMAIN}.storage",
                "version": 1,
                "data": {
                    "devices": [
                        {
                            "ieee": str(zigpy_dev.ieee),
                            "last_seen": last_seen,
                            "name": f"{zigpy_dev.manufacturer} {zigpy_dev.model}",
                        }
                    ],
                },
            }

        await setup_zha()
        zha_gateway = hass.data[zha_const.DATA_ZHA][zha_const.DATA_ZHA_GATEWAY]
        return zha_gateway.get_device(zigpy_dev.ieee)

    return _zha_device

@pytest.fixture(params=["zha_device_joined", "zha_device_restored"])
def zha_device_joined_restored(request):
    """Join or restore ZHA device."""
    named_method = request.getfixturevalue(request.param)
    named_method.name = request.param
    return named_method

@pytest.fixture
def zha_device_mock(hass, zigpy_device_mock):
    """Return a zha Device factory."""

    def _zha_device(
        endpoints=None,
        ieee="00:11:22:33:44:55:66:77",
        manufacturer="mock manufacturer",
        model="mock model",
        node_desc=b"\x02@\x807\x10\x7fd\x00\x00*d\x00\x00",
        patch_cluster=True,
    ):
        if endpoints is None:
            endpoints = {
                1: {
                    "in_clusters": [0, 1, 8, 768],
                    "out_clusters": [0x19],
                    "device_type": 0x0105,
                },
                2: {
                    "in_clusters": [0],
                    "out_clusters": [6, 8, 0x19, 768],
                    "device_type": 0x0810,
                },
            }
        zigpy_device = zigpy_device_mock(
            endpoints, ieee, manufacturer, model, node_desc, patch_cluster=patch_cluster
        )
        zha_device = zha_core_device.ZHADevice(hass, zigpy_device, MagicMock())
        return zha_device

    return _zha_device

@pytest.fixture
def hass_disable_services(hass):
    """Mock service register."""
    with patch.object(hass.services, "async_register"), patch.object(
        hass.services, "has_service", return_value=True
    ):
        yield hass

redpajama_set_name: RedPajamaGithub


text:
#ifndef DOMWindowCSS_h
#define DOMWindowCSS_h

#include "bindings/core/v8/ScriptWrappable.h"
#include "platform/heap/Handle.h"
#include "wtf/Forward.h"

namespace blink {

class DOMWindowCSS : public GarbageCollected<DOMWindowCSS>, public ScriptWrappable {
    DEFINE_WRAPPERTYPEINFO();
public:
    static bool supports(const String& property, const String& value);
    static bool supports(const String& conditionText);
    static String escape(const String& ident);

    DEFINE_INLINE_TRACE() { }

private:
    DOMWindowCSS() { }
};

} // namespace blink

#endif


text:
package node

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"time"

	"github.com/twmb/murmur3"
	"github.com/youzan/ZanRedisDB/common"
	"github.com/youzan/ZanRedisDB/engine"
	"github.com/youzan/ZanRedisDB/metric"
	"github.com/youzan/ZanRedisDB/transport/rafthttp"
	"golang.org/x/net/context"
)

var (
	ErrNamespaceAlreadyExist      = errors.New("namespace already exist")
	ErrNamespaceAlreadyStarting   = errors.New("namespace is starting")
	ErrRaftIDMismatch             = errors.New("raft id mismatch")
	ErrRaftConfMismatch           = errors.New("raft config mismatch")
	errTimeoutLeaderTransfer      = errors.New("raft leader transfer failed")
	errStopping                   = errors.New("ERR_CLUSTER_CHANGED: the namespace is stopping")
	ErrNamespaceNotFound          = errors.New("ERR_CLUSTER_CHANGED: namespace is not found")
	ErrNamespacePartitionNotFound = errors.New("ERR_CLUSTER_CHANGED: partition of the namespace is not found")
	ErrNamespaceNotLeader         = errors.New("ERR_CLUSTER_CHANGED: partition of the namespace is not leader on the node")
	ErrNodeNoLeader               = errors.New("ERR_CLUSTER_CHANGED: partition of the node has no leader")
	ErrRaftGroupNotReady          = errors.New("ERR_CLUSTER_CHANGED: raft group not ready")
	ErrProposalCanceled           = errors.New("ERR_CLUSTER_CHANGED: raft proposal " + context.Canceled.Error())
	errNamespaceConfInvalid       = errors.New("namespace config is invalid")
	ErrLocalMagicCodeConflict     = errors.New("namespace magic code conflict on local")
)

var perfLevel int32

type NamespaceNode struct {
	Node      *KVNode
	conf      *NamespaceConfig
	ready     int32
	magicCode int64
	nsDataDir string
}

func (nn *NamespaceNode) IsReady() bool {
	return atomic.LoadInt32(&nn.ready) == 1
}

func (nn *NamespaceNode) FullName() string {
	return nn.conf.Name
}

func (nn *NamespaceNode) SwitchForLearnerLeader(isLearnerLeader bool) {
	nn.Node.switchForLearnerLeader(isLearnerLeader)
}

func (nn *NamespaceNode) SetDynamicInfo(dync NamespaceDynamicConf) {
	nn.Node.SetDynamicInfo(dync)
}

func getMagicCodeFileName(dataPath string, grp string) string {
	return path.Join(dataPath, "magic_"+grp)
}

func saveMagicCode(fileName string, magicCode int64) error {
	var f *os.File
	var err error

	f, err = os.OpenFile(fileName, os.O_RDWR|os.O_CREATE, common.FILE_PERM)
	if err != nil {
		return err
	}
	defer f.Close()

	_, err = fmt.Fprintf(f, "%d\n",
		magicCode)
	if err != nil {
		return err
	}
	f.Sync()
	return nil
}

func LoadMagicCode(fileName string) (int64, error) {
	var f *os.File
	var err error

	f, err = os.OpenFile(fileName, os.O_RDONLY, common.FILE_PERM)
	if err != nil {
		if os.IsNotExist(err) {
			return 0, nil
		}
		return 0, err
	}
	defer f.Close()

	var code int64
	_, err = fmt.Fscanf(f, "%d\n",
		&code)
	if err != nil {
		return 0, err
	}
	return code, nil
}

func (nn *NamespaceNode) getMagicCode() int64 {
	return atomic.LoadInt64(&nn.magicCode)
}

func (nn *NamespaceNode) SetMagicCode(magic int64) error {
	fileName := getMagicCodeFileName(nn.nsDataDir, nn.conf.Name)
	if nn.getMagicCode() == 0 {
		// try read old magic code from file
		mc, err := LoadMagicCode(fileName)
		if err != nil {
			return err
		}
		atomic.CompareAndSwapInt64(&nn.magicCode, 0, mc)
	}
	// check if already magic code is different, if conflicted should report error
	if nn.getMagicCode() != 0 && nn.getMagicCode() != magic {
		nodeLog.Warningf("set magic code to %v conflict on local: %v", magic, nn.getMagicCode())
		return ErrLocalMagicCodeConflict
	}
	if nn.getMagicCode() == magic {
		return nil
	}
	changed := atomic.CompareAndSwapInt64(&nn.magicCode, 0, magic)
	if !changed {
		return fmt.Errorf("set magic code to %v conflict on local: %v", magic, nn.getMagicCode())
	}
	return saveMagicCode(fileName, magic)
}

func (nn *NamespaceNode) SetDataFixState(needFix bool) {
}

func (nn *NamespaceNode) GetLastLeaderChangedTime() int64 {
	return nn.Node.GetLastLeaderChangedTime()
}

func (nn *NamespaceNode) GetRaftID() uint64 {
	return nn.Node.rn.config.ID
}

func (nn *NamespaceNode) CheckRaftConf(raftID uint64, conf *NamespaceConfig) error {
	if nn.conf.EngType != conf.EngType ||
		nn.conf.RaftGroupConf.GroupID != conf.RaftGroupConf.GroupID {
		nodeLog.Infof("mine :%v, check raft conf:%v", nn.conf, conf)
		return ErrRaftConfMismatch
	}
	if raftID != nn.Node.rn.config.ID {
		nodeLog.Infof("mine :%v, check raft conf:%v", nn.Node.rn.config.ID, raftID)
		return ErrRaftIDMismatch
	}
	return nil
}

func (nn *NamespaceNode) Close() {
	nn.Node.Stop()
	atomic.StoreInt32(&nn.ready, 0)
	nodeLog.Infof("namespace stopped: %v", nn.conf.Name)
}

func (nn *NamespaceNode) Destroy() error {
	return nn.Node.destroy()
}

func (nn *NamespaceNode) IsDataNeedFix() bool {
	return false
}

// full ready node means: all local commit log replay done and we are aware of leader and
// maybe we have done all the newest commit log in state machine.
func (nn *NamespaceNode) IsNsNodeFullReady(checkCommitIndex bool) bool {
	if !nn.IsReady() {
		return false
	}
	if !nn.Node.rn.IsReplayFinished() {
		return false
	}
	return nn.Node.IsRaftSynced(checkCommitIndex)
}

func (nn *NamespaceNode) GetLearners() []*common.MemberInfo {
	return nn.Node.GetLearners()
}

func (nn *NamespaceNode) GetMembers() []*common.MemberInfo {
	return nn.Node.GetMembers()
}

func (nn *NamespaceNode) Start(forceStandaloneCluster bool) error {
	if !atomic.CompareAndSwapInt32(&nn.ready, 0, -1) {
		if atomic.LoadInt32(&nn.ready) == 1 {
			// already started
			return nil
		}
		// starting
		return ErrNamespaceAlreadyStarting
	}
	if err := nn.Node.Start(forceStandaloneCluster); err != nil {
		atomic.StoreInt32(&nn.ready, 0)
		return err
	}
	atomic.StoreInt32(&nn.ready, 1)
	return nil
}

func (nn *NamespaceNode) TransferMyLeader(to uint64, toRaftID uint64) error {
	err := nn.Node.TransferLeadership(toRaftID)
	if err != nil {
		return err
	}
	nodeLog.Infof("finished transfer to %v:%v", to, toRaftID)
	return nil
}

type NamespaceMeta struct {
	PartitionNum int
	walEng       engine.KVEngine
}

type NamespaceMgr struct {
	mutex         sync.RWMutex
	kvNodes       map[string]*NamespaceNode
	nsMetas       map[string]*NamespaceMeta
	groups        map[uint64]string
	machineConf   *MachineConfig
	raftTransport *rafthttp.Transport
	stopping      int32
	stopC         chan struct{}
	wg            sync.WaitGroup
	clusterInfo   common.IClusterInfo
	newLeaderChan chan string
}

func NewNamespaceMgr(transport *rafthttp.Transport, conf *MachineConfig) *NamespaceMgr {
	ns := &NamespaceMgr{
		kvNodes:       make(map[string]*NamespaceNode),
		groups:        make(map[uint64]string),
		nsMetas:       make(map[string]*NamespaceMeta),
		raftTransport: transport,
		machineConf:   conf,
		newLeaderChan: make(chan string, 2048),
	}
	regID, err := ns.LoadMachineRegID()
	if err != nil {
		nodeLog.Infof("load my register node id failed: %v", err)
	} else if regID > 0 {
		ns.machineConf.NodeID = regID
	}
	common.RegisterConfChangedHandler(common.ConfSlowLimiterSwitch, ns.HandleSlowLimiterSwitchChanged)
	RegisterSlowConfChanged()

	return ns
}

func (nsm *NamespaceMgr) SetDBOptions(key string, value string) error {
	key = strings.ToLower(key)
	switch key {
	case "rate_limiter_bytes_per_sec":
		bytes, err := strconv.ParseInt(value, 10, 64)
		if err != nil {
			return err
		}
		nsm.SetRateLimiterBytesPerSec(bytes)
	case "max_background_compactions":
		maxCompact, err := strconv.Atoi(value)
		if err != nil {
			return err
		}
		err = nsm.SetMaxBackgroundOptions(maxCompact, 0)
		if err != nil {
			return err
		}
	case "max_background_jobs":
		maxBackJobs, err := strconv.Atoi(value)
		if err != nil {
			return err
		}
		err = nsm.SetMaxBackgroundOptions(0, maxBackJobs)
		if err != nil {
			return err
		}
	default:
		return fmt.Errorf("db options %v not support", key)
	}
	return nil
}

func (nsm *NamespaceMgr) SetMaxBackgroundOptions(maxCompact int, maxBackJobs int) error {
	var err error
	nsm.mutex.RLock()
	defer nsm.mutex.RUnlock()
	for _, n := range nsm.kvNodes {
		if !n.IsReady() {
			continue
		}
		err = n.Node.SetMaxBackgroundOptions(maxCompact, maxBackJobs)
		if err != nil {
			break
		}
	}
	return err
}

func (nsm *NamespaceMgr) SetRateLimiterBytesPerSec(bytesPerSec int64) {
	if nsm.machineConf.RocksDBSharedConfig == nil {
		return
	}
	nsm.machineConf.RocksDBSharedConfig.ChangeLimiter(bytesPerSec)
}

func (nsm *NamespaceMgr) SetIClusterInfo(clusterInfo common.IClusterInfo) {
	nsm.clusterInfo = clusterInfo
}

func (nsm *NamespaceMgr) LoadMachineRegID() (uint64, error) {
	d, err := ioutil.ReadFile(
		path.Join(nsm.machineConf.DataRootDir, "myid"),
	)
	if err != nil {
		if os.IsNotExist(err) {
			return 0, nil
		}
		return 0, err
	}
	v, err := strconv.ParseInt(strings.Trim(string(d), "\r\n"), 10, 64)
	return uint64(v), err
}

func (nsm *NamespaceMgr) SaveMachineRegID(regID uint64) error {
	nsm.machineConf.NodeID = regID
	return ioutil.WriteFile(
		path.Join(nsm.machineConf.DataRootDir, "myid"),
		[]byte(strconv.FormatInt(int64(regID), 10)),
		common.FILE_PERM)
}

func (nsm *NamespaceMgr) GetDataRoot() string {
	return nsm.machineConf.DataRootDir
}

func (nsm *NamespaceMgr) CheckLocalNamespaces() map[string]int64 {
	// scan local namespace and magic number
	scanDir := nsm.machineConf.DataRootDir
	// scan all local undeleted ns-part dirs and read the magic code
	dirList, err := filepath.Glob(path.Join(scanDir, "*-*"))
	if err != nil {
		nodeLog.Infof("found local data root %s error: %v", scanDir, err.Error())
		return nil
	}
	magicList := make(map[string]int64)
	for _, dir := range dirList {
		nodeLog.Infof("found local dir in data root: %v", dir)
		grpName := path.Base(dir)
		ns, _ := common.GetNamespaceAndPartition(grpName)
		if ns != "" {
			code, err := LoadMagicCode(getMagicCodeFileName(dir, grpName))
			if err != nil {
				continue
			}
			nodeLog.Infof("add valid namespace magic : %v, %v", grpName, code)
			magicList[grpName] = code
		}
	}
	return magicList
}

func (nsm *NamespaceMgr) Start() {
	nsm.stopC = make(chan struct{})
	atomic.StoreInt32(&nsm.stopping, 0)
	nsm.mutex.Lock()
	for _, kv := range nsm.kvNodes {
		kv.Start(false)
	}
	nsm.mutex.Unlock()
	nsm.wg.Add(1)
	go func() {
		defer nsm.wg.Done()
		nsm.clearUnusedRaftPeer()
	}()

	nsm.wg.Add(1)
	go func() {
		defer nsm.wg.Done()
		nsm.checkNamespaceRaftLeader()
	}()

	nsm.wg.Add(1)
	go func() {
		defer nsm.wg.Done()
		nsm.processRaftTick()
	}()
}

func (nsm *NamespaceMgr) Stop() {
	if !atomic.CompareAndSwapInt32(&nsm.stopping, 0, 1) {
		return
	}
	close(nsm.stopC)
	tmp := nsm.GetNamespaces()
	for _, n := range tmp {
		n.Close()
	}
	nsm.wg.Wait()
	start := time.Now()
	for {
		// wait stopped callback done
		time.Sleep(time.Second)
		if len(nsm.GetNamespaces()) == 0 {
			break
		}
		if time.Since(start) > time.Second*10 {
			nodeLog.Warningf("some namespace not stopped while waiting timeout")
			break
		}
	}
	if nsm.machineConf.RocksDBSharedConfig != nil {
		nsm.machineConf.RocksDBSharedConfig.Destroy()
	}
	nsm.mutex.RLock()
	defer nsm.mutex.RUnlock()
	for _, meta := range nsm.nsMetas {
		if meta.walEng != nil {
			meta.walEng.CloseAll()
		}
	}
	if nsm.machineConf.WALRocksDBSharedConfig != nil {
		nsm.machineConf.WALRocksDBSharedConfig.Destroy()
	}
	nodeLog.Infof("namespace manager stopped")
}

func (nsm *NamespaceMgr) IsAllRecoveryDone() bool {
	done := true
	nsm.mutex.RLock()
	for _, n := range nsm.kvNodes {
		if !n.IsNsNodeFullReady(true) {
			done = false
			break
		}
	}
	nsm.mutex.RUnlock()
	return done
}

func (nsm *NamespaceMgr) GetNamespaces() map[string]*NamespaceNode {
	tmp := make(map[string]*NamespaceNode)
	nsm.mutex.RLock()
	for k, n := range nsm.kvNodes {
		tmp[k] = n
	}
	nsm.mutex.RUnlock()
	return tmp
}

func initRaftStorageEng(cfg *engine.RockEngConfig) engine.KVEngine {
	nodeLog.Infof("using rocksdb raft storage dir:%v", cfg.DataDir)
	cfg.DisableWAL = true
	cfg.DisableMergeCounter = true
	cfg.EnableTableCounter = false
	cfg.OptimizeFiltersForHits = true
	// basically, we no need compress wal since it will be cleaned after snapshot
	cfg.MinLevelToCompress = 5
	// TODO: check memtable_insert_with_hint_prefix_extractor and DeleteRange bug
	if cfg.InsertHintFixedLen == 0 {
		cfg.InsertHintFixedLen = 10
	}
	cfg.AutoCompacted = true
	db, err := engine.NewKVEng(cfg)
	if err == nil {
		err = db.OpenEng()
		if err == nil {
			db.SetOptsForLogStorage()
			go db.CompactAllRange()
			return db
		}
	}
	nodeLog.Warningf("failed to open rocks raft db: %v, fallback to memory entries", err.Error())
	return nil
}

func (nsm *NamespaceMgr) getWALEng(ns string, dataDir string, id uint64, gid uint32, meta *NamespaceMeta) engine.KVEngine {
	if !nsm.machineConf.UseRocksWAL || meta == nil {
		return nil
	}
	rsDir := path.Join(dataDir, "rswal", ns)

	if nsm.machineConf.SharedRocksWAL {
		if meta.walEng != nil {
			return meta.walEng
		}
	} else {
		sharding := fmt.Sprintf("%v-%v", gid, id)
		rsDir = path.Join(rsDir, sharding)
	}

	walEngCfg := engine.NewRockConfig()
	walEngCfg.DataDir = rsDir
	walEngCfg.RockOptions = nsm.machineConf.WALRocksDBOpts
	walEngCfg.SharedConfig = nsm.machineConf.WALRocksDBSharedConfig
	engine.FillDefaultOptions(&walEngCfg.RockOptions)
	eng := initRaftStorageEng(walEngCfg)
	if nsm.machineConf.SharedRocksWAL {
		meta.walEng = eng
	}
	return eng
}

func (nsm *NamespaceMgr) InitNamespaceNode(conf *NamespaceConfig, raftID uint64, join bool) (*NamespaceNode, error) {
	if atomic.LoadInt32(&nsm.stopping) == 1 {
		return nil, errStopping
	}

	expPolicy, err := common.StringToExpirationPolicy(conf.ExpirationPolicy)
	if err != nil {
		nodeLog.Infof("namespace %v invalid expire policy : %v", conf.Name, conf.ExpirationPolicy)
		return nil, err
	}

	dv, err := common.StringToDataVersionType(conf.DataVersion)
	if err != nil {
		nodeLog.Infof("namespace %v invalid data version: %v", conf.Name, conf.DataVersion)
		return nil, err
	}
	if expPolicy == common.WaitCompact && dv == common.DefaultDataVer {
		return nil, errors.New("can not use compact ttl for old data version")
	}
	if dv != common.DefaultDataVer {
		nodeLog.Infof("namespace %v data version: %v, expire policy: %v", conf.Name, conf.DataVersion, expPolicy)
	}

	kvOpts := &KVOptions{
		DataDir:          path.Join(nsm.machineConf.DataRootDir, conf.Name),
		KeepBackup:       nsm.machineConf.KeepBackup,
		EngType:          conf.EngType,
		RockOpts:         nsm.machineConf.RocksDBOpts,
		ExpirationPolicy: expPolicy,
		DataVersion:      dv,
		SharedConfig:     nsm.machineConf.RocksDBSharedConfig,
	}
	engine.FillDefaultOptions(&kvOpts.RockOpts)

	if conf.PartitionNum <= 0 {
		return nil, errNamespaceConfInvalid
	}
	if conf.Replicator <= 0 {
		return nil, errNamespaceConfInvalid
	}
	clusterNodes := make(map[uint64]ReplicaInfo)
	for _, v := range conf.RaftGroupConf.SeedNodes {
		clusterNodes[v.ReplicaID] = v
	}
	_, ok := clusterNodes[uint64(raftID)]
	if !ok {
		join = true
	}

	nsm.mutex.Lock()
	defer nsm.mutex.Unlock()
	if n, ok := nsm.kvNodes[conf.Name]; ok {
		return n, ErrNamespaceAlreadyExist
	}

	d, _ := json.MarshalIndent(&conf, "", " ")
	nodeLog.Infof("namespace load config: %v", string(d))
	d, _ = json.MarshalIndent(&kvOpts, "", " ")
	nodeLog.Infof("namespace kv config: %v", string(d))
	nodeLog.Infof("local namespace node %v start with raft cluster: %v", raftID, clusterNodes)
	raftConf := &RaftConfig{
		GroupID:        conf.RaftGroupConf.GroupID,
		GroupName:      conf.Name,
		ID:             uint64(raftID),
		RaftAddr:       nsm.machineConf.LocalRaftAddr,
		DataDir:        kvOpts.DataDir,
		RaftPeers:      clusterNodes,
		SnapCount:      conf.SnapCount,
		SnapCatchup:    conf.SnapCatchup,
		Replicator:     int32(conf.Replicator),
		OptimizedFsync: conf.OptimizedFsync,
		nodeConfig:     nsm.machineConf,
	}
	d, _ = json.MarshalIndent(&raftConf, "", " ")
	nodeLog.Infof("namespace raft config: %v", string(d))

	var meta *NamespaceMeta
	if oldMeta, ok := nsm.nsMetas[conf.BaseName]; !ok {
		meta = &NamespaceMeta{
			PartitionNum: conf.PartitionNum,
		}
		nsm.nsMetas[conf.BaseName] = meta
		nodeLog.Infof("namespace meta init: %v", conf)
	} else {
		if oldMeta.PartitionNum != conf.PartitionNum {
			nodeLog.Errorf("namespace meta mismatch: %v, old: %v", conf, oldMeta)
			// update the meta if mismatch, it may happen if create the same namespace with different
			// config for old deleted namespace
			if oldMeta.walEng != nil {
				oldMeta.walEng.CloseAll()
			}
			meta = &NamespaceMeta{
				PartitionNum: conf.PartitionNum,
			}
			nsm.nsMetas[conf.BaseName] = meta
		} else {
			meta = oldMeta
		}
	}
	rs := nsm.getWALEng(conf.BaseName, nsm.machineConf.DataRootDir, raftConf.ID, uint32(raftConf.GroupID), meta)
	raftConf.SetEng(rs)

	kv, err := NewKVNode(kvOpts, raftConf, nsm.raftTransport,
		join, nsm.onNamespaceStopped(raftConf.GroupID, conf.Name),
		nsm.clusterInfo, nsm.newLeaderChan)
	if err != nil {
		return nil, err
	}

	n := &NamespaceNode{
		Node:      kv,
		conf:      conf,
		nsDataDir: kvOpts.DataDir,
	}

	nsm.kvNodes[conf.Name] = n
	nsm.groups[raftConf.GroupID] = conf.Name
	return n, nil
}

func HashedKey(pk []byte) int {
	return int(murmur3.Sum32(pk))
}
func GetHashedPartitionID(pk []byte, pnum int) int {
	return HashedKey(pk) % pnum
}

func (nsm *NamespaceMgr) GetNamespaceNodeWithPrimaryKeySum(nsBaseName string, pk []byte, pkSum int) (*NamespaceNode, error) {
	nsm.mutex.RLock()
	defer nsm.mutex.RUnlock()
	v, ok := nsm.nsMetas[nsBaseName]
	if !ok {
		nodeLog.Infof("namespace %v meta not found", nsBaseName)
		return nil, ErrNamespaceNotFound
	}
	pid := pkSum % v.PartitionNum
	fullName := common.GetNsDesp(nsBaseName, pid)
	n, ok := nsm.kvNodes[fullName]
	if !ok {
		nodeLog.Debugf("namespace %v partition %v not found for pk: %v", nsBaseName, pid, string(pk))
		return nil, ErrNamespacePartitionNotFound
	}
	if !n.IsReady() {
		return nil, ErrRaftGroupNotReady
	}
	return n, nil
}

func (nsm *NamespaceMgr) GetNamespaceNodeWithPrimaryKey(nsBaseName string, pk []byte) (*NamespaceNode, error) {
	pkSum := HashedKey(pk)
	return nsm.GetNamespaceNodeWithPrimaryKeySum(nsBaseName, pk, pkSum)
}

func (nsm *NamespaceMgr) GetNamespaceNodes(nsBaseName string, leaderOnly bool) (map[string]*NamespaceNode, error) {
	nsNodes := make(map[string]*NamespaceNode)

	tmp := nsm.GetNamespaces()
	for k, v := range tmp {
		ns, _ := common.GetNamespaceAndPartition(k)
		if ns == nsBaseName && v.IsReady() {
			if leaderOnly && !v.Node.IsLead() {
				continue
			}
			nsNodes[k] = v
		}
	}

	if len(nsNodes) <= 0 {
		return nil, ErrNamespaceNotFound
	}

	return nsNodes, nil
}

func (nsm *NamespaceMgr) GetNamespaceNode(ns string) *NamespaceNode {
	nsm.mutex.RLock()
	v, _ := nsm.kvNodes[ns]
	nsm.mutex.RUnlock()
	if v != nil {
		if !v.IsReady() {
			return nil
		}
	}
	return v
}

func (nsm *NamespaceMgr) GetNamespaceNodeFromGID(gid uint64) *NamespaceNode {
	nsm.mutex.RLock()
	defer nsm.mutex.RUnlock()
	gn, ok := nsm.groups[gid]
	if !ok {
		nodeLog.Debugf("group name not found %v ", gid)
		return nil
	}
	kv, ok := nsm.kvNodes[gn]
	if !ok {
		nodeLog.Infof("kv namespace not found %v ", gn)
		return nil
	}

	if !kv.IsReady() {
		return nil
	}
	return kv
}

func (nsm *NamespaceMgr) GetWALDBStats(leaderOnly bool) map[string]map[string]interface{} {
	nsm.mutex.RLock()
	nsStats := make(map[string]map[string]interface{}, len(nsm.kvNodes))
	for k, n := range nsm.kvNodes {
		if !n.IsReady() {
			continue
		}
		if leaderOnly && !n.Node.IsLead() {
			continue
		}
		dbStats := n.Node.GetWALDBInternalStats()
		nsStats[k] = dbStats
	}
	nsm.mutex.RUnlock()
	return nsStats
}

func (nsm *NamespaceMgr) GetDBStats(leaderOnly bool) map[string]string {
	nsm.mutex.RLock()
	nsStats := make(map[string]string, len(nsm.kvNodes))
	for k, n := range nsm.kvNodes {
		if !n.IsReady() {
			continue
		}
		if leaderOnly && !n.Node.IsLead() {
			continue
		}
		dbStats := n.Node.GetDBInternalStats()
		nsStats[k] = dbStats
	}
	nsm.mutex.RUnlock()
	return nsStats
}

func (nsm *NamespaceMgr) GetLogSyncStatsInSyncer() ([]metric.LogSyncStats, []metric.LogSyncStats) {
	nsm.mutex.RLock()
	nsRecvStats := make([]metric.LogSyncStats, 0, len(nsm.kvNodes))
	nsSyncStats := make([]metric.LogSyncStats, 0, len(nsm.kvNodes))
	for k, n := range nsm.kvNodes {
		if !n.IsReady() {
			continue
		}
		recvStats, syncStats := n.Node.GetLogSyncStatsInSyncLearner()
		if recvStats == nil || syncStats == nil {
			continue
		}
		recvStats.Name = k
		syncStats.Name = k
		nsRecvStats = append(nsRecvStats, *recvStats)
		nsSyncStats = append(nsSyncStats, *syncStats)
	}
	nsm.mutex.RUnlock()
	return nsRecvStats, nsSyncStats
}

func (nsm *NamespaceMgr) GetLogSyncStats(leaderOnly bool, srcClusterName string) []metric.LogSyncStats {
	if srcClusterName == "" {
		return nil
	}
	nsm.mutex.RLock()
	nsStats := make([]metric.LogSyncStats, 0, len(nsm.kvNodes))
	for k, n := range nsm.kvNodes {
		if !n.IsReady() {
			continue
		}
		if leaderOnly && !n.Node.IsLead() {
			continue
		}
		term, index, ts := n.Node.GetRemoteClusterSyncedRaft(srcClusterName)
		if term == 0 && index == 0 {
			continue
		}
		var s metric.LogSyncStats
		s.Name = k
		s.IsLeader = n.Node.IsLead()
		s.Term = term
		s.Index = index
		s.Timestamp = ts
		nsStats = append(nsStats, s)
	}
	nsm.mutex.RUnlock()
	return nsStats
}

func (nsm *NamespaceMgr) GetStats(leaderOnly bool, table string, needTableDetail bool) []metric.NamespaceStats {
	nsm.mutex.RLock()
	nsStats := make([]metric.NamespaceStats, 0, len(nsm.kvNodes))
	for k, n := range nsm.kvNodes {
		if !n.IsReady() {
			continue
		}
		if leaderOnly && !n.Node.IsLead() {
			continue
		}
		ns := n.Node.GetStats(table, needTableDetail)
		ns.Name = k
		ns.EngType = n.conf.EngType
		ns.IsLeader = n.Node.IsLead()
		nsStats = append(nsStats, ns)
	}
	nsm.mutex.RUnlock()
	return nsStats
}

func (nsm *NamespaceMgr) getNsNodeList(ns string) []*NamespaceNode {
	nsm.mutex.RLock()
	nodeList := make([]*NamespaceNode, 0, len(nsm.kvNodes))
	for k, n := range nsm.kvNodes {
		baseName, _ := common.GetNamespaceAndPartition(k)
		if ns != "" && ns != baseName {
			continue
		}
		nodeList = append(nodeList, n)
	}
	nsm.mutex.RUnlock()
	return nodeList
}

func (nsm *NamespaceMgr) BackupDB(ns string, checkLast bool) {
	nodeList := nsm.getNsNodeList(ns)
	for _, n := range nodeList {
		if atomic.LoadInt32(&nsm.stopping) == 1 {
			return
		}
		if n.IsReady() {
			n.Node.BackupDB(checkLast)
		}
	}
}

func (nsm *NamespaceMgr) EnableTopn(ns string, on bool) {
	nodeList := nsm.getNsNodeList(ns)
	for _, n := range nodeList {
		if atomic.LoadInt32(&nsm.stopping) == 1 {
			return
		}
		if n.IsReady() {
			n.Node.sm.EnableTopn(on)
		}
	}
}

func (nsm *NamespaceMgr) ClearTopn(ns string) {
	nodeList := nsm.getNsNodeList(ns)
	for _, n := range nodeList {
		if atomic.LoadInt32(&nsm.stopping) == 1 {
			return
		}
		if n.IsReady() {
			n.Node.sm.ClearTopn()
		}
	}
}

func (nsm *NamespaceMgr) DisableOptimizeDB(disable bool) {
	nodeList := nsm.getNsNodeList("")
	for _, n := range nodeList {
		if atomic.LoadInt32(&nsm.stopping) == 1 {
			return
		}
		if n.IsReady() {
			n.Node.DisableOptimizeDB(disable)
		}
	}
}

func (nsm *NamespaceMgr) OptimizeDB(ns string, table string) {
	nodeList := nsm.getNsNodeList(ns)
	for _, n := range nodeList {
		if atomic.LoadInt32(&nsm.stopping) == 1 {
			return
		}
		if n.IsReady() {
			n.Node.OptimizeDB(table)
		}
	}
}

func (nsm *NamespaceMgr) OptimizeDBExpire(ns string) {
	nodeList := nsm.getNsNodeList(ns)
	for _, n := range nodeList {
		if atomic.LoadInt32(&nsm.stopping) == 1 {
			return
		}
		if n.IsReady() {
			n.Node.OptimizeDBExpire()
		}
	}
}

func (nsm *NamespaceMgr) OptimizeDBAnyRange(ns string, r CompactAPIRange) {
	nodeList := nsm.getNsNodeList(ns)
	for _, n := range nodeList {
		if atomic.LoadInt32(&nsm.stopping) == 1 {
			return
		}
		if n.IsReady() {
			n.Node.OptimizeDBAnyRange(r)
		}
	}
}

func (nsm *NamespaceMgr) DeleteRange(ns string, dtr DeleteTableRange) error {
	if ns == "" {
		return errors.New("namespace can not be empty")
	}
	nodeList := nsm.getNsNodeList(ns)
	for _, n := range nodeList {
		if atomic.LoadInt32(&nsm.stopping) == 1 {
			return common.ErrStopped
		}
		if n.IsReady() {
			err := n.Node.DeleteRange(dtr)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

// this is used to clean some shared namespace data bewteen all partitions, only remove them until
// all the partitions are removed and the whole namespace is deleted from cluster
func (nsm *NamespaceMgr) CleanSharedNsFiles(baseNS string) error {
	nsm.mutex.Lock()
	defer nsm.mutex.Unlock()
	// check if all parts of this namespace is deleted, if so we should remove meta
	for fullName, _ := range nsm.kvNodes {
		n, _ := common.GetNamespaceAndPartition(fullName)
		if n == baseNS {
			err := fmt.Errorf("some node is still running while clean namespace files: %s, %s", baseNS, fullName)
			nodeLog.Info(err.Error())
			return err
		}
	}
	meta, ok := nsm.nsMetas[baseNS]
	if ok && meta.walEng != nil {
		nodeLog.Infof("all partitions of namespace %v stopped, removing shared data", baseNS)
		meta.walEng.CloseAll()
	}
	delete(nsm.nsMetas, baseNS)
	if nsm.machineConf.UseRocksWAL && baseNS != "" {
		// clean the rock wal files
		rsDir := path.Join(nsm.machineConf.DataRootDir, "rswal", baseNS)
		ts := strconv.Itoa(int(time.Now().UnixNano()))
		err := os.Rename(rsDir,
			rsDir+"-deleted-"+ts)
		if err != nil {
			nodeLog.Warningf("remove shared data failed: %s", err.Error())
			return err
		}
		nodeLog.Infof("remove shared wal data for namespace: %s", baseNS)
	}
	return nil
}

func (nsm *NamespaceMgr) onNamespaceStopped(gid uint64, ns string) func() {
	return func() {
		nsm.mutex.Lock()
		defer nsm.mutex.Unlock()
		_, ok := nsm.kvNodes[ns]
		if !ok {
			return
		}
		// note we should not delete any data here, since it is not known we just stopped or it is destroyed
		nodeLog.Infof("namespace stopped: %v-%v", ns, gid)
		nsm.kvNodes[ns] = nil
		delete(nsm.kvNodes, ns)
		delete(nsm.groups, gid)
		baseNS, _ := common.GetNamespaceAndPartition(ns)
		meta, ok := nsm.nsMetas[baseNS]
		if !ok {
			return
		}
		found := false
		// check if all parts of this namespace is deleted, if so we should remove meta
		for fullName, _ := range nsm.kvNodes {
			n, _ := common.GetNamespaceAndPartition(fullName)
			if n == baseNS {
				found = true
				break
			}
		}
		if !found {
			nodeLog.Infof("all partitions of namespace %v stopped, removing meta", baseNS)
			if meta.walEng != nil {
				meta.walEng.CloseAll()
			}
			delete(nsm.nsMetas, baseNS)
		}
	}
}

func (nsm *NamespaceMgr) processRaftTick() {
	ticker := time.NewTicker(time.Duration(nsm.machineConf.TickMs) * time.Millisecond)
	defer ticker.Stop()
	for {
		select {
		case <-ticker.C:
			// send tick for all raft group
			nsm.mutex.RLock()
			nodes := make([]*KVNode, 0, len(nsm.kvNodes))
			for _, v := range nsm.kvNodes {
				if v.IsReady() {
					nodes = append(nodes, v.Node)
				}
			}
			nsm.mutex.RUnlock()
			for _, n := range nodes {
				n.Tick()
			}

		case <-nsm.stopC:
			return
		}
	}
}

func (nsm *NamespaceMgr) checkNamespaceRaftLeader() {
	ticker := time.NewTicker(time.Second * 15)
	defer ticker.Stop()
	leaderNodes := make([]*NamespaceNode, 0)
	// report to leader may failed, so we need retry and check period
	doCheck := func() {
		leaderNodes = leaderNodes[:0]
		nsm.mutex.RLock()
		for _, v := range nsm.kvNodes {
			if v.IsReady() {
				if v.Node.IsLead() {
					leaderNodes = append(leaderNodes, v)
				}
			}
		}
		nsm.mutex.RUnlock()
		for _, v := range leaderNodes {
			v.Node.ReportMeLeaderToCluster()
		}
	}

	for {
		select {
		case <-ticker.C:
			doCheck()
		case ns := <-nsm.newLeaderChan:
			nsm.mutex.RLock()
			v, ok := nsm.kvNodes[ns]
			nsm.mutex.RUnlock()
			if !ok {
				nodeLog.Infof("leader changed namespace not found: %v", ns)
			} else if v.IsReady() {
				v.Node.OnRaftLeaderChanged()
			}
		case <-nsm.stopC:
			return
		}
	}
}

func (nsm *NamespaceMgr) clearUnusedRaftPeer() {
	ticker := time.NewTicker(time.Minute)
	defer ticker.Stop()
	// while close or remove raft node, we need check if any remote transport peer
	// should be closed.
	doCheck := func() {
		nsm.mutex.RLock()
		defer nsm.mutex.RUnlock()
		peers := nsm.raftTransport.GetAllPeers()
		currentNodeIDs := make(map[uint64]bool)
		for _, v := range nsm.kvNodes {
			mems := v.Node.GetMembers()
			for _, m := range mems {
				currentNodeIDs[m.NodeID] = true
			}
			// handle learners here
			mems = v.Node.GetLearners()
			for _, m := range mems {
				currentNodeIDs[m.NodeID] = true
			}
		}
		for _, p := range peers {
			if _, ok := currentNodeIDs[uint64(p)]; !ok {
				nodeLog.Infof("remove peer %v from transport since no any raft is related", p)
				nsm.raftTransport.RemovePeer(p)
			}
		}
	}

	for {
		select {
		case <-ticker.C:
			doCheck()
		case <-nsm.stopC:
			return
		}
	}
}

func (nsm *NamespaceMgr) HandleSlowLimiterSwitchChanged(v interface{}) {
	nodeLog.Infof("config for slow limiter changed to : %v", v)
	on, ok := v.(int)
	if !ok {
		return
	}
	nsm.mutex.RLock()
	for _, n := range nsm.kvNodes {
		if !n.IsReady() || n.Node == nil {
			continue
		}
		if on > 0 {
			n.Node.slowLimiter.TurnOn()
		} else {
			n.Node.slowLimiter.TurnOff()
		}
	}
	nsm.mutex.RUnlock()
}


text:
package org.plugin.cloud.db.dao;

public interface MasterDAO {

}


text:
#include <grpc/support/port_platform.h>

#include "src/core/ext/transport/chttp2/transport/hpack_encoder.h"

#include <algorithm>
#include <cstdint>
#include <memory>

#include "absl/utility/utility.h"

#include <grpc/slice.h>
#include <grpc/slice_buffer.h>
#include <grpc/support/log.h>

#include "src/core/ext/transport/chttp2/transport/bin_encoder.h"
#include "src/core/ext/transport/chttp2/transport/frame.h"
#include "src/core/ext/transport/chttp2/transport/hpack_constants.h"
#include "src/core/ext/transport/chttp2/transport/hpack_encoder_table.h"
#include "src/core/ext/transport/chttp2/transport/varint.h"
#include "src/core/lib/debug/stats.h"
#include "src/core/lib/iomgr/exec_ctx.h"
#include "src/core/lib/surface/validate_metadata.h"
#include "src/core/lib/transport/timeout_encoding.h"

namespace grpc_core {

namespace {

constexpr size_t kDataFrameHeaderSize = 9;

} /* namespace */

/* fills p (which is expected to be kDataFrameHeaderSize bytes long)
 * with a data frame header */
static void FillHeader(uint8_t* p, uint8_t type, uint32_t id, size_t len,
                       uint8_t flags) {
  /* len is the current frame size (i.e. for the frame we're finishing).
     We finish a frame if:
     1) We called ensure_space(), (i.e. add_tiny_header_data()) and adding
        'need_bytes' to the frame would cause us to exceed max_frame_size.
     2) We called add_header_data, and adding the slice would cause us to exceed
        max_frame_size.
     3) We're done encoding the header.

     Thus, len is always <= max_frame_size.
     max_frame_size is derived from GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE,
     which has a max allowable value of 16777215 (see chttp_transport.cc).
     Thus, the following assert can be a debug assert. */
  GPR_DEBUG_ASSERT(len < 16777316);
  *p++ = static_cast<uint8_t>(len >> 16);
  *p++ = static_cast<uint8_t>(len >> 8);
  *p++ = static_cast<uint8_t>(len);
  *p++ = type;
  *p++ = flags;
  *p++ = static_cast<uint8_t>(id >> 24);
  *p++ = static_cast<uint8_t>(id >> 16);
  *p++ = static_cast<uint8_t>(id >> 8);
  *p++ = static_cast<uint8_t>(id);
}

size_t HPackCompressor::Framer::CurrentFrameSize() const {
  const size_t frame_size =
      output_->length - prefix_.output_length_at_start_of_frame;
  GPR_DEBUG_ASSERT(frame_size <= max_frame_size_);
  return frame_size;
}

// finish a frame - fill in the previously reserved header
void HPackCompressor::Framer::FinishFrame(bool is_header_boundary) {
  const uint8_t type = is_first_frame_ ? GRPC_CHTTP2_FRAME_HEADER
                                       : GRPC_CHTTP2_FRAME_CONTINUATION;
  uint8_t flags = 0;
  // per the HTTP/2 spec:
  //   A HEADERS frame carries the END_STREAM flag that signals the end of a
  //   stream. However, a HEADERS frame with the END_STREAM flag set can be
  //   followed by CONTINUATION frames on the same stream. Logically, the
  //   CONTINUATION frames are part of the HEADERS frame.
  // Thus, we add the END_STREAM flag to the HEADER frame (the first frame).
  if (is_first_frame_ && is_end_of_stream_) {
    flags |= GRPC_CHTTP2_DATA_FLAG_END_STREAM;
  }
  // per the HTTP/2 spec:
  //   A HEADERS frame without the END_HEADERS flag set MUST be followed by
  //   a CONTINUATION frame for the same stream.
  // Thus, we add the END_HEADER flag to the last frame.
  if (is_header_boundary) {
    flags |= GRPC_CHTTP2_DATA_FLAG_END_HEADERS;
  }
  FillHeader(GRPC_SLICE_START_PTR(output_->slices[prefix_.header_idx]), type,
             stream_id_, CurrentFrameSize(), flags);
  stats_->framing_bytes += kDataFrameHeaderSize;
  is_first_frame_ = false;
}

// begin a new frame: reserve off header space, remember how many bytes we'd
// output before beginning
HPackCompressor::Framer::FramePrefix HPackCompressor::Framer::BeginFrame() {
  grpc_slice reserved;
  reserved.refcount = nullptr;
  reserved.data.inlined.length = kDataFrameHeaderSize;
  return FramePrefix{grpc_slice_buffer_add_indexed(output_, reserved),
                     output_->length};
}

// make sure that the current frame is of the type desired, and has sufficient
// space to add at least about_to_add bytes -- finishes the current frame if
// needed
void HPackCompressor::Framer::EnsureSpace(size_t need_bytes) {
  if (GPR_LIKELY(CurrentFrameSize() + need_bytes <= max_frame_size_)) {
    return;
  }
  FinishFrame(false);
  prefix_ = BeginFrame();
}

void HPackCompressor::Framer::Add(Slice slice) {
  while (true) {
    const size_t len = slice.length();
    if (len == 0) return;
    const size_t remaining = max_frame_size_ - CurrentFrameSize();
    if (len <= remaining) {
      stats_->header_bytes += len;
      grpc_slice_buffer_add(output_, slice.TakeCSlice());
      return;
    } else {
      stats_->header_bytes += remaining;
      Slice tail = slice.Split(remaining);
      grpc_slice_buffer_add(output_, slice.TakeCSlice());
      slice = std::move(tail);
      FinishFrame(false);
      prefix_ = BeginFrame();
    }
  }
}

uint8_t* HPackCompressor::Framer::AddTiny(size_t len) {
  EnsureSpace(len);
  stats_->header_bytes += len;
  return grpc_slice_buffer_tiny_add(output_, len);
}

void HPackCompressor::Framer::EmitIndexed(uint32_t elem_index) {
  GRPC_STATS_INC_HPACK_SEND_INDEXED();
  VarintWriter<1> w(elem_index);
  w.Write(0x80, AddTiny(w.length()));
}

struct WireValue {
  WireValue(uint8_t huffman_prefix, bool insert_null_before_wire_value,
            Slice slice)
      : data(std::move(slice)),
        huffman_prefix(huffman_prefix),
        insert_null_before_wire_value(insert_null_before_wire_value),
        length(data.length() + (insert_null_before_wire_value ? 1 : 0)) {}
  Slice data;
  const uint8_t huffman_prefix;
  const bool insert_null_before_wire_value;
  const size_t length;
};

static WireValue GetWireValue(Slice value, bool true_binary_enabled,
                              bool is_bin_hdr) {
  if (is_bin_hdr) {
    if (true_binary_enabled) {
      GRPC_STATS_INC_HPACK_SEND_BINARY();
      return WireValue(0x00, true, std::move(value));
    } else {
      GRPC_STATS_INC_HPACK_SEND_BINARY_BASE64();
      return WireValue(0x80, false,
                       Slice(grpc_chttp2_base64_encode_and_huffman_compress(
                           value.c_slice())));
    }
  } else {
    /* TODO(ctiller): opportunistically compress non-binary headers */
    GRPC_STATS_INC_HPACK_SEND_UNCOMPRESSED();
    return WireValue(0x00, false, std::move(value));
  }
}

struct DefinitelyInterned {
  static bool IsBinary(grpc_slice key) {
    return grpc_is_refcounted_slice_binary_header(key);
  }
};
struct UnsureIfInterned {
  static bool IsBinary(grpc_slice key) {
    return grpc_is_binary_header_internal(key);
  }
};

class BinaryStringValue {
 public:
  explicit BinaryStringValue(Slice value, bool use_true_binary_metadata)
      : wire_value_(
            GetWireValue(std::move(value), use_true_binary_metadata, true)),
        len_val_(wire_value_.length) {}

  size_t prefix_length() const {
    return len_val_.length() +
           (wire_value_.insert_null_before_wire_value ? 1 : 0);
  }

  void WritePrefix(uint8_t* prefix_data) {
    len_val_.Write(wire_value_.huffman_prefix, prefix_data);
    if (wire_value_.insert_null_before_wire_value) {
      prefix_data[len_val_.length()] = 0;
    }
  }

  Slice data() { return std::move(wire_value_.data); }

 private:
  WireValue wire_value_;
  VarintWriter<1> len_val_;
};

class NonBinaryStringValue {
 public:
  explicit NonBinaryStringValue(Slice value)
      : value_(std::move(value)), len_val_(value_.length()) {}

  size_t prefix_length() const { return len_val_.length(); }

  void WritePrefix(uint8_t* prefix_data) { len_val_.Write(0x00, prefix_data); }

  Slice data() { return std::move(value_); }

 private:
  Slice value_;
  VarintWriter<1> len_val_;
};

class StringKey {
 public:
  explicit StringKey(Slice key)
      : key_(std::move(key)), len_key_(key_.length()) {}

  size_t prefix_length() const { return 1 + len_key_.length(); }

  void WritePrefix(uint8_t type, uint8_t* data) {
    data[0] = type;
    len_key_.Write(0x00, data + 1);
  }

  Slice key() { return std::move(key_); }

 private:
  Slice key_;
  VarintWriter<1> len_key_;
};

void HPackCompressor::Framer::EmitLitHdrWithNonBinaryStringKeyIncIdx(
    Slice key_slice, Slice value_slice) {
  GRPC_STATS_INC_HPACK_SEND_LITHDR_INCIDX_V();
  GRPC_STATS_INC_HPACK_SEND_UNCOMPRESSED();
  StringKey key(std::move(key_slice));
  key.WritePrefix(0x40, AddTiny(key.prefix_length()));
  Add(key.key());
  NonBinaryStringValue emit(std::move(value_slice));
  emit.WritePrefix(AddTiny(emit.prefix_length()));
  Add(emit.data());
}

void HPackCompressor::Framer::EmitLitHdrWithBinaryStringKeyNotIdx(
    Slice key_slice, Slice value_slice) {
  GRPC_STATS_INC_HPACK_SEND_LITHDR_NOTIDX_V();
  GRPC_STATS_INC_HPACK_SEND_UNCOMPRESSED();
  StringKey key(std::move(key_slice));
  key.WritePrefix(0x00, AddTiny(key.prefix_length()));
  Add(key.key());
  BinaryStringValue emit(std::move(value_slice), use_true_binary_metadata_);
  emit.WritePrefix(AddTiny(emit.prefix_length()));
  Add(emit.data());
}

void HPackCompressor::Framer::EmitLitHdrWithBinaryStringKeyIncIdx(
    Slice key_slice, Slice value_slice) {
  GRPC_STATS_INC_HPACK_SEND_LITHDR_INCIDX_V();
  GRPC_STATS_INC_HPACK_SEND_UNCOMPRESSED();
  StringKey key(std::move(key_slice));
  key.WritePrefix(0x40, AddTiny(key.prefix_length()));
  Add(key.key());
  BinaryStringValue emit(std::move(value_slice), use_true_binary_metadata_);
  emit.WritePrefix(AddTiny(emit.prefix_length()));
  Add(emit.data());
}

void HPackCompressor::Framer::EmitLitHdrWithBinaryStringKeyNotIdx(
    uint32_t key_index, Slice value_slice) {
  GRPC_STATS_INC_HPACK_SEND_LITHDR_NOTIDX();
  GRPC_STATS_INC_HPACK_SEND_UNCOMPRESSED();
  BinaryStringValue emit(std::move(value_slice), use_true_binary_metadata_);
  VarintWriter<4> key(key_index);
  uint8_t* data = AddTiny(key.length() + emit.prefix_length());
  key.Write(0x00, data);
  emit.WritePrefix(data + key.length());
  Add(emit.data());
}

void HPackCompressor::Framer::EmitLitHdrWithNonBinaryStringKeyNotIdx(
    Slice key_slice, Slice value_slice) {
  GRPC_STATS_INC_HPACK_SEND_LITHDR_NOTIDX_V();
  GRPC_STATS_INC_HPACK_SEND_UNCOMPRESSED();
  StringKey key(std::move(key_slice));
  key.WritePrefix(0x00, AddTiny(key.prefix_length()));
  Add(key.key());
  NonBinaryStringValue emit(std::move(value_slice));
  emit.WritePrefix(AddTiny(emit.prefix_length()));
  Add(emit.data());
}

void HPackCompressor::Framer::AdvertiseTableSizeChange() {
  VarintWriter<3> w(compressor_->table_.max_size());
  w.Write(0x20, AddTiny(w.length()));
}

void HPackCompressor::SliceIndex::EmitTo(absl::string_view key,
                                         const Slice& value, Framer* framer) {
  auto& table = framer->compressor_->table_;
  using It = std::vector<ValueIndex>::iterator;
  It prev = values_.end();
  uint32_t transport_length =
      key.length() + value.length() + hpack_constants::kEntryOverhead;
  if (transport_length > HPackEncoderTable::MaxEntrySize()) {
    framer->EmitLitHdrWithNonBinaryStringKeyNotIdx(Slice::FromStaticString(key),
                                                   value.Ref());
    return;
  }
  // Linear scan through previous values to see if we find the value.
  for (It it = values_.begin(); it != values_.end(); ++it) {
    if (value == it->value) {
      // Got a hit... is it still in the decode table?
      if (table.ConvertableToDynamicIndex(it->index)) {
        // Yes, emit the index and proceed to cleanup.
        framer->EmitIndexed(table.DynamicIndex(it->index));
      } else {
        // Not current, emit a new literal and update the index.
        it->index = table.AllocateIndex(transport_length);
        framer->EmitLitHdrWithNonBinaryStringKeyIncIdx(
            Slice::FromStaticString(key), value.Ref());
      }
      // Bubble this entry up if we can - ensures that the most used values end
      // up towards the start of the array.
      if (prev != values_.end()) std::swap(*prev, *it);
      // If there are entries at the end of the array, and those entries are no
      // longer in the table, remove them.
      while (!values_.empty() &&
             !table.ConvertableToDynamicIndex(values_.back().index)) {
        values_.pop_back();
      }
      // All done, early out.
      return;
    }
    prev = it;
  }
  // No hit, emit a new literal and add it to the index.
  uint32_t index = table.AllocateIndex(transport_length);
  framer->EmitLitHdrWithNonBinaryStringKeyIncIdx(Slice::FromStaticString(key),
                                                 value.Ref());
  values_.emplace_back(value.Ref(), index);
}

void HPackCompressor::Framer::Encode(const Slice& key, const Slice& value) {
  if (absl::EndsWith(key.as_string_view(), "-bin")) {
    EmitLitHdrWithBinaryStringKeyNotIdx(key.Ref(), value.Ref());
  } else {
    EmitLitHdrWithNonBinaryStringKeyNotIdx(key.Ref(), value.Ref());
  }
}

void HPackCompressor::Framer::Encode(HttpPathMetadata, const Slice& value) {
  compressor_->path_index_.EmitTo(HttpPathMetadata::key(), value, this);
}

void HPackCompressor::Framer::Encode(HttpAuthorityMetadata,
                                     const Slice& value) {
  compressor_->authority_index_.EmitTo(HttpAuthorityMetadata::key(), value,
                                       this);
}

void HPackCompressor::Framer::Encode(TeMetadata, TeMetadata::ValueType value) {
  GPR_ASSERT(value == TeMetadata::ValueType::kTrailers);
  EncodeAlwaysIndexed(
      &compressor_->te_index_, "te", Slice::FromStaticString("trailers"),
      2 /* te */ + 8 /* trailers */ + hpack_constants::kEntryOverhead);
}

void HPackCompressor::Framer::Encode(ContentTypeMetadata,
                                     ContentTypeMetadata::ValueType value) {
  if (value != ContentTypeMetadata::ValueType::kApplicationGrpc) {
    gpr_log(GPR_ERROR, "Not encoding bad content-type header");
    return;
  }
  EncodeAlwaysIndexed(&compressor_->content_type_index_, "content-type",
                      Slice::FromStaticString("application/grpc"),
                      12 /* content-type */ + 16 /* application/grpc */ +
                          hpack_constants::kEntryOverhead);
}

void HPackCompressor::Framer::Encode(HttpSchemeMetadata,
                                     HttpSchemeMetadata::ValueType value) {
  switch (value) {
    case HttpSchemeMetadata::ValueType::kHttp:
      EmitIndexed(6);  // :scheme: http
      break;
    case HttpSchemeMetadata::ValueType::kHttps:
      EmitIndexed(7);  // :scheme: https
      break;
    case HttpSchemeMetadata::ValueType::kInvalid:
      GPR_ASSERT(false);
      break;
  }
}

void HPackCompressor::Framer::Encode(GrpcTraceBinMetadata, const Slice& slice) {
  EncodeRepeatingSliceValue(GrpcTraceBinMetadata::key(), slice,
                            &compressor_->grpc_trace_bin_index_,
                            HPackEncoderTable::MaxEntrySize());
}

void HPackCompressor::Framer::Encode(GrpcTagsBinMetadata, const Slice& slice) {
  EncodeRepeatingSliceValue(GrpcTagsBinMetadata::key(), slice,
                            &compressor_->grpc_tags_bin_index_,
                            HPackEncoderTable::MaxEntrySize());
}

void HPackCompressor::Framer::Encode(HttpStatusMetadata, uint32_t status) {
  if (status == 200) {
    EmitIndexed(8);  // :status: 200
    return;
  }
  uint8_t index = 0;
  switch (status) {
    case 204:
      index = 9;  // :status: 204
      break;
    case 206:
      index = 10;  // :status: 206
      break;
    case 304:
      index = 11;  // :status: 304
      break;
    case 400:
      index = 12;  // :status: 400
      break;
    case 404:
      index = 13;  // :status: 404
      break;
    case 500:
      index = 14;  // :status: 500
      break;
  }
  if (GPR_LIKELY(index != 0)) {
    EmitIndexed(index);
  } else {
    EmitLitHdrWithNonBinaryStringKeyIncIdx(Slice::FromStaticString(":status"),
                                           Slice::FromInt64(status));
  }
}

void HPackCompressor::Framer::Encode(HttpMethodMetadata,
                                     HttpMethodMetadata::ValueType method) {
  switch (method) {
    case HttpMethodMetadata::ValueType::kPost:
      EmitIndexed(3);  // :method: POST
      break;
    case HttpMethodMetadata::ValueType::kGet:
      EmitIndexed(2);  // :method: GET
      break;
    case HttpMethodMetadata::ValueType::kPut:
      // Right now, we only emit PUT as a method for testing purposes, so it's
      // fine to not index it.
      EmitLitHdrWithNonBinaryStringKeyNotIdx(Slice::FromStaticString(":method"),
                                             Slice::FromStaticString("PUT"));
      break;
    case HttpMethodMetadata::ValueType::kInvalid:
      GPR_ASSERT(false);
      break;
  }
}

void HPackCompressor::Framer::EncodeAlwaysIndexed(uint32_t* index,
                                                  absl::string_view key,
                                                  Slice value,
                                                  uint32_t transport_length) {
  if (compressor_->table_.ConvertableToDynamicIndex(*index)) {
    EmitIndexed(compressor_->table_.DynamicIndex(*index));
  } else {
    *index = compressor_->table_.AllocateIndex(transport_length);
    EmitLitHdrWithNonBinaryStringKeyIncIdx(Slice::FromStaticString(key),
                                           std::move(value));
  }
}

void HPackCompressor::Framer::EncodeIndexedKeyWithBinaryValue(
    uint32_t* index, absl::string_view key, Slice value) {
  if (compressor_->table_.ConvertableToDynamicIndex(*index)) {
    EmitLitHdrWithBinaryStringKeyNotIdx(
        compressor_->table_.DynamicIndex(*index), std::move(value));
  } else {
    *index = compressor_->table_.AllocateIndex(key.length() + value.length() +
                                               hpack_constants::kEntryOverhead);
    EmitLitHdrWithBinaryStringKeyIncIdx(Slice::FromStaticString(key),
                                        std::move(value));
  }
}

void HPackCompressor::Framer::EncodeRepeatingSliceValue(
    const absl::string_view& key, const Slice& slice, uint32_t* index,
    size_t max_compression_size) {
  if (hpack_constants::SizeForEntry(key.size(), slice.size()) >
      max_compression_size) {
    EmitLitHdrWithBinaryStringKeyNotIdx(Slice::FromStaticString(key),
                                        slice.Ref());
  } else {
    EncodeIndexedKeyWithBinaryValue(index, key, slice.Ref());
  }
}

void HPackCompressor::Framer::Encode(GrpcTimeoutMetadata, Timestamp deadline) {
  Timeout timeout = Timeout::FromDuration(deadline - ExecCtx::Get()->Now());
  for (auto it = compressor_->previous_timeouts_.begin();
       it != compressor_->previous_timeouts_.end(); ++it) {
    double ratio = timeout.RatioVersus(it->timeout);
    // If the timeout we're sending is shorter than a previous timeout, but
    // within 3% of it, we'll consider sending it.
    if (ratio > -3 && ratio <= 0 &&
        compressor_->table_.ConvertableToDynamicIndex(it->index)) {
      EmitIndexed(compressor_->table_.DynamicIndex(it->index));
      // Put this timeout to the front of the queue - forces common timeouts to
      // be considered earlier.
      std::swap(*it, *compressor_->previous_timeouts_.begin());
      return;
    }
  }
  // Clean out some expired timeouts.
  while (!compressor_->previous_timeouts_.empty() &&
         !compressor_->table_.ConvertableToDynamicIndex(
             compressor_->previous_timeouts_.back().index)) {
    compressor_->previous_timeouts_.pop_back();
  }
  Slice encoded = timeout.Encode();
  uint32_t index = compressor_->table_.AllocateIndex(
      GrpcTimeoutMetadata::key().length() + encoded.length() +
      hpack_constants::kEntryOverhead);
  compressor_->previous_timeouts_.push_back(PreviousTimeout{timeout, index});
  EmitLitHdrWithNonBinaryStringKeyIncIdx(
      Slice::FromStaticString(GrpcTimeoutMetadata::key()), std::move(encoded));
}

void HPackCompressor::Framer::Encode(UserAgentMetadata, const Slice& slice) {
  if (hpack_constants::SizeForEntry(UserAgentMetadata::key().size(),
                                    slice.size()) >
      HPackEncoderTable::MaxEntrySize()) {
    EmitLitHdrWithNonBinaryStringKeyNotIdx(
        Slice::FromStaticString(UserAgentMetadata::key()), slice.Ref());
    return;
  }
  if (!slice.is_equivalent(compressor_->user_agent_)) {
    compressor_->user_agent_ = slice.Ref();
    compressor_->user_agent_index_ = 0;
  }
  EncodeAlwaysIndexed(&compressor_->user_agent_index_, UserAgentMetadata::key(),
                      slice.Ref(),
                      hpack_constants::SizeForEntry(
                          UserAgentMetadata::key().size(), slice.size()));
}

void HPackCompressor::Framer::Encode(GrpcStatusMetadata,
                                     grpc_status_code status) {
  const uint32_t code = static_cast<uint32_t>(status);
  uint32_t* index = nullptr;
  if (code < kNumCachedGrpcStatusValues) {
    index = &compressor_->cached_grpc_status_[code];
    if (compressor_->table_.ConvertableToDynamicIndex(*index)) {
      EmitIndexed(compressor_->table_.DynamicIndex(*index));
      return;
    }
  }
  Slice key = Slice::FromStaticString(GrpcStatusMetadata::key());
  Slice value = Slice::FromInt64(code);
  const uint32_t transport_length =
      key.length() + value.length() + hpack_constants::kEntryOverhead;
  if (index != nullptr) {
    *index = compressor_->table_.AllocateIndex(transport_length);
    EmitLitHdrWithNonBinaryStringKeyIncIdx(std::move(key), std::move(value));
  } else {
    EmitLitHdrWithNonBinaryStringKeyNotIdx(std::move(key), std::move(value));
  }
}

void HPackCompressor::Framer::Encode(GrpcEncodingMetadata,
                                     grpc_compression_algorithm value) {
  uint32_t* index = nullptr;
  if (value < GRPC_COMPRESS_ALGORITHMS_COUNT) {
    index = &compressor_->cached_grpc_encoding_[static_cast<uint32_t>(value)];
    if (compressor_->table_.ConvertableToDynamicIndex(*index)) {
      EmitIndexed(compressor_->table_.DynamicIndex(*index));
      return;
    }
  }
  auto key = Slice::FromStaticString(GrpcEncodingMetadata::key());
  auto encoded_value = GrpcEncodingMetadata::Encode(value);
  uint32_t transport_length =
      key.length() + encoded_value.length() + hpack_constants::kEntryOverhead;
  if (index != nullptr) {
    *index = compressor_->table_.AllocateIndex(transport_length);
    EmitLitHdrWithNonBinaryStringKeyIncIdx(std::move(key),
                                           std::move(encoded_value));
  } else {
    EmitLitHdrWithNonBinaryStringKeyNotIdx(std::move(key),
                                           std::move(encoded_value));
  }
}

void HPackCompressor::Framer::Encode(GrpcAcceptEncodingMetadata,
                                     CompressionAlgorithmSet value) {
  if (compressor_->grpc_accept_encoding_index_ != 0 &&
      value == compressor_->grpc_accept_encoding_ &&
      compressor_->table_.ConvertableToDynamicIndex(
          compressor_->grpc_accept_encoding_index_)) {
    EmitIndexed(compressor_->table_.DynamicIndex(
        compressor_->grpc_accept_encoding_index_));
    return;
  }
  auto key = Slice::FromStaticString(GrpcAcceptEncodingMetadata::key());
  auto encoded_value = GrpcAcceptEncodingMetadata::Encode(value);
  uint32_t transport_length =
      key.length() + encoded_value.length() + hpack_constants::kEntryOverhead;
  compressor_->grpc_accept_encoding_index_ =
      compressor_->table_.AllocateIndex(transport_length);
  compressor_->grpc_accept_encoding_ = value;
  EmitLitHdrWithNonBinaryStringKeyIncIdx(std::move(key),
                                         std::move(encoded_value));
}

void HPackCompressor::SetMaxUsableSize(uint32_t max_table_size) {
  max_usable_size_ = max_table_size;
  SetMaxTableSize(std::min(table_.max_size(), max_table_size));
}

void HPackCompressor::SetMaxTableSize(uint32_t max_table_size) {
  if (table_.SetMaxSize(std::min(max_usable_size_, max_table_size))) {
    advertise_table_size_change_ = true;
    if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace)) {
      gpr_log(GPR_INFO, "set max table size from encoder to %d",
              max_table_size);
    }
  }
}

HPackCompressor::Framer::Framer(const EncodeHeaderOptions& options,
                                HPackCompressor* compressor,
                                grpc_slice_buffer* output)
    : max_frame_size_(options.max_frame_size),
      use_true_binary_metadata_(options.use_true_binary_metadata),
      is_end_of_stream_(options.is_end_of_stream),
      stream_id_(options.stream_id),
      output_(output),
      stats_(options.stats),
      compressor_(compressor),
      prefix_(BeginFrame()) {
  if (absl::exchange(compressor_->advertise_table_size_change_, false)) {
    AdvertiseTableSizeChange();
  }
}

}  // namespace grpc_core


text:
package org.apache.hadoop.yarn.client.api.async;

import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.classification.InterfaceAudience.Private;
import org.apache.hadoop.classification.InterfaceAudience.Public;
import org.apache.hadoop.classification.InterfaceStability.Stable;
import org.apache.hadoop.classification.InterfaceStability.Unstable;
import org.apache.hadoop.service.AbstractService;
import org.apache.hadoop.yarn.api.protocolrecords.RegisterApplicationMasterResponse;
import org.apache.hadoop.yarn.api.records.Container;
import org.apache.hadoop.yarn.api.records.ContainerId;
import org.apache.hadoop.yarn.api.records.ContainerStatus;
import org.apache.hadoop.yarn.api.records.ContainerUpdateType;
import org.apache.hadoop.yarn.api.records.ExecutionType;
import org.apache.hadoop.yarn.api.records.FinalApplicationStatus;
import org.apache.hadoop.yarn.api.records.NodeReport;
import org.apache.hadoop.yarn.api.records.Priority;
import org.apache.hadoop.yarn.api.records.Resource;
import org.apache.hadoop.yarn.api.records.UpdateContainerRequest;
import org.apache.hadoop.yarn.api.records.UpdatedContainer;
import org.apache.hadoop.yarn.client.api.AMRMClient;
import org.apache.hadoop.yarn.client.api.AMRMClient.ContainerRequest;
import org.apache.hadoop.yarn.client.api.TimelineV2Client;
import org.apache.hadoop.yarn.client.api.async.impl.AMRMClientAsyncImpl;
import org.apache.hadoop.yarn.client.api.impl.AMRMClientImpl;
import org.apache.hadoop.yarn.exceptions.YarnException;
import org.apache.hadoop.yarn.util.resource.Resources;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;

/**
 * AMRMClientAsync handles communication with the ResourceManager
 * and provides asynchronous updates on events such as container allocations and
 * completions.  It contains a thread that sends periodic heartbeats to the
 * ResourceManager.
 *
 * It should be used by implementing a CallbackHandler:
 *

 * {@code
 * class MyCallbackHandler extends AMRMClientAsync.AbstractCallbackHandler {
 *   public void onContainersAllocated(List<Container> containers) {
 *     [run tasks on the containers]
 *   }
 *
 *   public void onContainersUpdated(List<Container> containers) {
 *     [determine if resource allocation of containers have been increased in
 *      the ResourceManager, and if so, inform the NodeManagers to increase the
 *      resource monitor/enforcement on the containers]
 *   }
 *
 *   public void onContainersCompleted(List<ContainerStatus> statuses) {
 *     [update progress, check whether app is done]
 *   }
 *
 *   public void onNodesUpdated(List<NodeReport> updated) {}
 *
 *   public void onReboot() {}
 * }
 * }
 *

 *
 * The client's lifecycle should be managed similarly to the following:
 *
 *

 * {@code
 * AMRMClientAsync asyncClient =
 *     createAMRMClientAsync(appAttId, 1000, new MyCallbackhandler());
 * asyncClient.init(conf);
 * asyncClient.start();
 * RegisterApplicationMasterResponse response = asyncClient
 *    .registerApplicationMaster(appMasterHostname, appMasterRpcPort,
 *       appMasterTrackingUrl);
 * asyncClient.addContainerRequest(containerRequest);
 * [... wait for application to complete]
 * asyncClient.unregisterApplicationMaster(status, appMsg, trackingUrl);
 * asyncClient.stop();
 * }
 *

 */
@Public
@Stable
public abstract class AMRMClientAsync<T extends ContainerRequest>
extends AbstractService {
  private static final Log LOG = LogFactory.getLog(AMRMClientAsync.class);

  protected final AMRMClient<T> client;
  protected final CallbackHandler handler;
  protected final AtomicInteger heartbeatIntervalMs = new AtomicInteger();

  /**
   *
Create a new instance of AMRMClientAsync.

   *
   * @param intervalMs heartbeat interval in milliseconds between AM and RM
   * @param callbackHandler callback handler that processes responses from
   *                        the ResourceManager
   */
  public static <T extends ContainerRequest> AMRMClientAsync<T>
      createAMRMClientAsync(
      int intervalMs, AbstractCallbackHandler callbackHandler) {
    return new AMRMClientAsyncImpl<T>(intervalMs, callbackHandler);
  }

  /**
   *
Create a new instance of AMRMClientAsync.

   *
   * @param client the AMRMClient instance
   * @param intervalMs heartbeat interval in milliseconds between AM and RM
   * @param callbackHandler callback handler that processes responses from
   *                        the ResourceManager
   */
  public static <T extends ContainerRequest> AMRMClientAsync<T>
      createAMRMClientAsync(
      AMRMClient<T> client, int intervalMs,
      AbstractCallbackHandler callbackHandler) {
    return new AMRMClientAsyncImpl<T>(client, intervalMs, callbackHandler);
  }

  protected AMRMClientAsync(
      int intervalMs, AbstractCallbackHandler callbackHandler) {
    this(new AMRMClientImpl<T>(), intervalMs, callbackHandler);
  }

  @Private
  @VisibleForTesting
  protected AMRMClientAsync(AMRMClient<T> client, int intervalMs,
      AbstractCallbackHandler callbackHandler) {
    super(AMRMClientAsync.class.getName());
    this.client = client;
    this.heartbeatIntervalMs.set(intervalMs);
    this.handler = callbackHandler;
  }

  /**
   *
   * @deprecated Use {@link #createAMRMClientAsync(int,
   *             AMRMClientAsync.AbstractCallbackHandler)} instead.
   */
  @Deprecated
  public static <T extends ContainerRequest> AMRMClientAsync<T>
      createAMRMClientAsync(int intervalMs, CallbackHandler callbackHandler) {
    return new AMRMClientAsyncImpl<T>(intervalMs, callbackHandler);
  }

  /**
   *
   * @deprecated Use {@link #createAMRMClientAsync(AMRMClient,
   *             int, AMRMClientAsync.AbstractCallbackHandler)} instead.
   */
  @Deprecated
  public static <T extends ContainerRequest> AMRMClientAsync<T>
      createAMRMClientAsync(AMRMClient<T> client, int intervalMs,
          CallbackHandler callbackHandler) {
    return new AMRMClientAsyncImpl<T>(client, intervalMs, callbackHandler);
  }

  @Deprecated
  protected AMRMClientAsync(int intervalMs, CallbackHandler callbackHandler) {
    this(new AMRMClientImpl<T>(), intervalMs, callbackHandler);
  }

  @Private
  @VisibleForTesting
  @Deprecated
  protected AMRMClientAsync(AMRMClient<T> client, int intervalMs,
      CallbackHandler callbackHandler) {
    super(AMRMClientAsync.class.getName());
    this.client = client;
    this.heartbeatIntervalMs.set(intervalMs);
    this.handler = callbackHandler;
  }

  public void setHeartbeatInterval(int interval) {
    heartbeatIntervalMs.set(interval);
  }

  public abstract List<? extends Collection<T>> getMatchingRequests(
                                                   Priority priority,
                                                   String resourceName,
                                                   Resource capability);

  /**
   * Returns all matching ContainerRequests that match the given Priority,
   * ResourceName, ExecutionType and Capability.
   *
   * NOTE: This matches only requests that were made by the client WITHOUT the
   * allocationRequestId specified.
   *
   * @param priority Priority.
   * @param resourceName Location.
   * @param executionType ExecutionType.
   * @param capability Capability.
   * @return All matching ContainerRequests
   */
  public List<? extends Collection<T>> getMatchingRequests(
      Priority priority, String resourceName, ExecutionType executionType,
      Resource capability) {
    return client.getMatchingRequests(priority, resourceName,
        executionType, capability);
  }

  /**
   * Returns all matching ContainerRequests that match the given
   * AllocationRequestId.
   *
   * NOTE: This matches only requests that were made by the client WITH the
   * allocationRequestId specified.
   *
   * @param allocationRequestId AllocationRequestId.
   * @return All matching ContainerRequests
   */
  public Collection<T> getMatchingRequests(long allocationRequestId) {
    return client.getMatchingRequests(allocationRequestId);
  }

  /**
   * Registers this application master with the resource manager. On successful
   * registration, starts the heartbeating thread.
   * @throws YarnException
   * @throws IOException
   */
  public abstract RegisterApplicationMasterResponse registerApplicationMaster(
      String appHostName, int appHostPort, String appTrackingUrl)
      throws YarnException, IOException;

  /**
   * Unregister the application master. This must be called in the end.
   * @param appStatus Success/Failure status of the master
   * @param appMessage Diagnostics message on failure
   * @param appTrackingUrl New URL to get master info
   * @throws YarnException
   * @throws IOException
   */
  public abstract void unregisterApplicationMaster(
      FinalApplicationStatus appStatus, String appMessage, String appTrackingUrl)
  throws YarnException, IOException;

  /**
   * Request containers for resources before calling allocate
   * @param req Resource request
   */
  public abstract void addContainerRequest(T req);

  /**
   * Remove previous container request. The previous container request may have
   * already been sent to the ResourceManager. So even after the remove request
   * the app must be prepared to receive an allocation for the previous request
   * even after the remove request
   * @param req Resource request
   */
  public abstract void removeContainerRequest(T req);

  /**
   * Request container resource change before calling allocate.
   * Any previous pending resource change request of the same container will be
   * removed.
   *
   * Application that calls this method is expected to maintain the
   * Containers that are returned from previous successful
   * allocations or resource changes. By passing in the existing container and a
   * target resource capability to this method, the application requests the
   * ResourceManager to change the existing resource allocation to the target
   * resource allocation.
   *
   * @deprecated use
   * {@link #requestContainerUpdate(Container, UpdateContainerRequest)}
   *
   * @param container The container returned from the last successful resource
   *                  allocation or resource change
   * @param capability  The target resource capability of the container
   */
  @Deprecated
  public void requestContainerResourceChange(
      Container container, Resource capability) {
    Preconditions.checkNotNull(container, "Container cannot be null!!");
    Preconditions.checkNotNull(capability,
        "UpdateContainerRequest cannot be null!!");
    requestContainerUpdate(container, UpdateContainerRequest.newInstance(
        container.getVersion(), container.getId(),
        Resources.fitsIn(capability, container.getResource()) ?
            ContainerUpdateType.DECREASE_RESOURCE :
            ContainerUpdateType.INCREASE_RESOURCE,
        capability, null));
  }

  /**
   * Request a container update before calling allocate.
   * Any previous pending update request of the same container will be
   * removed.
   *
   * @param container The container returned from the last successful resource
   *                  allocation or update
   * @param updateContainerRequest The UpdateContainerRequest.
   */
  public abstract void requestContainerUpdate(
      Container container, UpdateContainerRequest updateContainerRequest);

  /**
   * Release containers assigned by the Resource Manager. If the app cannot use
   * the container or wants to give up the container then it can release them.
   * The app needs to make new requests for the released resource capability if
   * it still needs it. eg. it released non-local resources
   * @param containerId
   */
  public abstract void releaseAssignedContainer(ContainerId containerId);

  /**
   * Get the currently available resources in the cluster.
   * A valid value is available after a call to allocate has been made
   * @return Currently available resources
   */
  public abstract Resource getAvailableResources();

  /**
   * Get the current number of nodes in the cluster.
   * A valid values is available after a call to allocate has been made
   * @return Current number of nodes in the cluster
   */
  public abstract int getClusterNodeCount();

  /**
   * Register TimelineClient to AMRMClient.
   * @param timelineClient
   * @throws YarnException when this method is invoked even when ATS V2 is not
   *           configured.
   */
  public void registerTimelineV2Client(TimelineV2Client timelineClient)
      throws YarnException {
    client.registerTimelineV2Client(timelineClient);
  }

  /**
   * Get registered timeline client.
   * @return the registered timeline client
   */
  public TimelineV2Client getRegisteredTimelineV2Client() {
    return client.getRegisteredTimelineV2Client();
  }

  /**
   * Update application's blacklist with addition or removal resources.
   *
   * @param blacklistAdditions list of resources which should be added to the
   *        application blacklist
   * @param blacklistRemovals list of resources which should be removed from the
   *        application blacklist
   */
  public abstract void updateBlacklist(List<String> blacklistAdditions,
                                       List<String> blacklistRemovals);

  /**
   * Wait for check to return true for each 1000 ms.
   * See also {@link #waitFor(java.util.function.Supplier, int)}
   * and {@link #waitFor(java.util.function.Supplier, int, int)}
   * @param check the condition for which it should wait
   */
  public void waitFor(Supplier<Boolean> check) throws InterruptedException {
    waitFor(check, 1000);
  }

  /**
   * Wait for check to return true for each
   * checkEveryMillis ms.
   * See also {@link #waitFor(java.util.function.Supplier, int, int)}
   * @param check user defined checker
   * @param checkEveryMillis interval to call check
   */
  public void waitFor(Supplier<Boolean> check, int checkEveryMillis)
      throws InterruptedException {
    waitFor(check, checkEveryMillis, 1);
  };

  /**
   * Wait for check to return true for each
   * checkEveryMillis ms. In the main loop, this method will log
   * the message "waiting in main loop" for each logInterval times
   * iteration to confirm the thread is alive.
   * @param check user defined checker
   * @param checkEveryMillis interval to call check
   * @param logInterval interval to log for each
   */
  public void waitFor(Supplier<Boolean> check, int checkEveryMillis,
      int logInterval) throws InterruptedException {
    Preconditions.checkNotNull(check, "check should not be null");
    Preconditions.checkArgument(checkEveryMillis >= 0,
        "checkEveryMillis should be positive value");
    Preconditions.checkArgument(logInterval >= 0,
        "logInterval should be positive value");

    int loggingCounter = logInterval;
    do {
      if (LOG.isDebugEnabled()) {
        LOG.debug("Check the condition for main loop.");
      }

      boolean result = check.get();
      if (result) {
        LOG.info("Exits the main loop.");
        return;
      }
      if (--loggingCounter <= 0) {
        LOG.info("Waiting in main loop.");
        loggingCounter = logInterval;
      }

      Thread.sleep(checkEveryMillis);
    } while (true);
  }

  /**
   *

   * The callback abstract class. The callback functions need to be implemented
   * by {@link AMRMClientAsync} users. The APIs are called when responses from
   * the ResourceManager are available.
   *

   */
  public abstract static class AbstractCallbackHandler
      implements CallbackHandler {

    /**
     * Called when the ResourceManager responds to a heartbeat with completed
     * containers. If the response contains both completed containers and
     * allocated containers, this will be called before containersAllocated.
     */
    public abstract void onContainersCompleted(List<ContainerStatus> statuses);

    /**
     * Called when the ResourceManager responds to a heartbeat with allocated
     * containers. If the response containers both completed containers and
     * allocated containers, this will be called after containersCompleted.
     */
    public abstract void onContainersAllocated(List<Container> containers);

    /**
     * Called when the ResourceManager responds to a heartbeat with containers
     * whose resource allocation has been changed.
     */
    @Public
    @Unstable
    public abstract void onContainersUpdated(List<UpdatedContainer> containers);

    /**
     * Called when the ResourceManager wants the ApplicationMaster to shutdown
     * for being out of sync etc. The ApplicationMaster should not unregister
     * with the RM unless the ApplicationMaster wants to be the last attempt.
     */
    public abstract void onShutdownRequest();

    /**
     * Called when nodes tracked by the ResourceManager have changed in health,
     * availability etc.
     */
    public abstract void onNodesUpdated(List<NodeReport> updatedNodes);

    public abstract float getProgress();

    /**
     * Called when error comes from RM communications as well as from errors in
     * the callback itself from the app. Calling
     * stop() is the recommended action.
     */
    public abstract void onError(Throwable e);
  }

  /**
   * @deprecated Use {@link AMRMClientAsync.AbstractCallbackHandler} instead.
   */
  @Deprecated
  public interface CallbackHandler {

    /**
     * Called when the ResourceManager responds to a heartbeat with completed
     * containers. If the response contains both completed containers and
     * allocated containers, this will be called before containersAllocated.
     */
    void onContainersCompleted(List<ContainerStatus> statuses);

    /**
     * Called when the ResourceManager responds to a heartbeat with allocated
     * containers. If the response containers both completed containers and
     * allocated containers, this will be called after containersCompleted.
     */
    void onContainersAllocated(List<Container> containers);

    /**
     * Called when the ResourceManager wants the ApplicationMaster to shutdown
     * for being out of sync etc. The ApplicationMaster should not unregister
     * with the RM unless the ApplicationMaster wants to be the last attempt.
     */
    void onShutdownRequest();

    /**
     * Called when nodes tracked by the ResourceManager have changed in health,
     * availability etc.
     */
    void onNodesUpdated(List<NodeReport> updatedNodes);

    float getProgress();

    /**
     * Called when error comes from RM communications as well as from errors in
     * the callback itself from the app. Calling
     * stop() is the recommended action.
     *
     * @param e
     */
    void onError(Throwable e);
  }
}


text:
using System;
using System.Linq;
using System.Net.Sockets;
using NUnit.Framework;
using Quartz;

namespace CrystalQuartz.Core.Quartz3.Tests
{
    [TestFixture]
    public class Quartz3SchedulerEngineTests
    {

    }
}


text:
import { ProjectTypeFilterPipe } from './project-type-filter.pipe';
import { Project } from './models/project';

describe('ProjectTypeFilterPipe', () => {

  const string = 'Energy-Electricity';
  let value: Project[];
  let expectedResponse: Project[];

  const pipe = new ProjectTypeFilterPipe();

  describe('given a valid response', () => {
    it('returns 1 item', () => {
      value = [new Project({type: 'Energy-Electricity'})];
      expectedResponse = [new Project({type: 'Energy-Electricity'})];

      expect(pipe.transform(value, string).length).toBe(expectedResponse.length);
    });
    it('returns n items', () => {
      value = [
        new Project({type: 'Energy-Electricity'}),
        new Project({type: 'Energy-Electricity'}),
        new Project({type: 'Energy-Electricity'}),
        new Project({type: 'Energy-Electricity'})
      ];
      expectedResponse = [
        new Project({type: 'Energy-Electricity'}),
        new Project({type: 'Energy-Electricity'}),
        new Project({type: 'Energy-Electricity'}),
        new Project({type: 'Energy-Electricity'})
      ];

      expect(pipe.transform(value, string).length).toBe(expectedResponse.length);
    });
  });
  describe('given a mix of different types', () => {
    beforeEach(() => {
      value = [
        new Project({type: 'Energy-Electricity'}),
        new Project({type: 'Industrial'}),
        new Project({type: 'Energy-Electricity'}),
        new Project({type: 'Industrial'})
      ];
      expectedResponse = [
        new Project({type: 'Energy-Electricity'}),
        new Project({type: 'Energy-Electricity'}),
      ];
    });
    it('returns two items', () => {
      expect(pipe.transform(value, string).length).toBe(expectedResponse.length);
    });
    it('returns Project with same type value as value passed in', () => {
      expect(JSON.stringify(pipe.transform(value, string)[0].type))
        .toBe(JSON.stringify(expectedResponse[0].type));
      expect(JSON.stringify(pipe.transform(value, string)[1].type))
      .toBe(JSON.stringify(expectedResponse[1].type));
    });
  });
});


text:
class ProbModel {
public:
	virtual ~ProbModel() {}
	virtual max_size_t GenRate() = 0;
};

#endif // PROBMODEL_H


text:
#ifndef ApplyStyleCommand_h
#define ApplyStyleCommand_h

#include "core/editing/WritingDirection.h"
#include "core/editing/commands/CompositeEditCommand.h"
#include "core/html/HTMLElement.h"

namespace blink {

class EditingStyle;
class HTMLSpanElement;
class StyleChange;

enum ShouldIncludeTypingStyle { IncludeTypingStyle, IgnoreTypingStyle };

class CORE_EXPORT ApplyStyleCommand final : public CompositeEditCommand {
 public:
  enum EPropertyLevel { PropertyDefault, ForceBlockProperties };
  enum InlineStyleRemovalMode { RemoveIfNeeded, RemoveAlways, RemoveNone };
  enum EAddStyledElement { AddStyledElement, DoNotAddStyledElement };
  typedef bool (*IsInlineElementToRemoveFunction)(const Element*);

  static ApplyStyleCommand* create(Document& document,
                                   const EditingStyle* style,
                                   InputEvent::InputType inputType,
                                   EPropertyLevel level = PropertyDefault) {
    return new ApplyStyleCommand(document, style, inputType, level);
  }
  static ApplyStyleCommand* create(Document& document,
                                   const EditingStyle* style,
                                   const Position& start,
                                   const Position& end) {
    return new ApplyStyleCommand(document, style, start, end);
  }
  static ApplyStyleCommand* create(Element* element, bool removeOnly) {
    return new ApplyStyleCommand(element, removeOnly);
  }
  static ApplyStyleCommand* create(
      Document& document,
      const EditingStyle* style,
      IsInlineElementToRemoveFunction isInlineElementToRemoveFunction,
      InputEvent::InputType inputType) {
    return new ApplyStyleCommand(document, style,
                                 isInlineElementToRemoveFunction, inputType);
  }

  DECLARE_VIRTUAL_TRACE();

 private:
  ApplyStyleCommand(Document&,
                    const EditingStyle*,
                    InputEvent::InputType,
                    EPropertyLevel);
  ApplyStyleCommand(Document&,
                    const EditingStyle*,
                    const Position& start,
                    const Position& end);
  ApplyStyleCommand(Element*, bool removeOnly);
  ApplyStyleCommand(Document&,
                    const EditingStyle*,
                    bool (*isInlineElementToRemove)(const Element*),
                    InputEvent::InputType);

  void doApply(EditingState*) override;
  InputEvent::InputType inputType() const override;

  // style-removal helpers
  bool isStyledInlineElementToRemove(Element*) const;
  bool shouldApplyInlineStyleToRun(EditingStyle*,
                                   Node* runStart,
                                   Node* pastEndNode);
  void removeConflictingInlineStyleFromRun(EditingStyle*,
                                           Member<Node>& runStart,
                                           Member<Node>& runEnd,
                                           Node* pastEndNode,
                                           EditingState*);
  bool removeInlineStyleFromElement(EditingStyle*,
                                    HTMLElement*,
                                    EditingState*,
                                    InlineStyleRemovalMode = RemoveIfNeeded,
                                    EditingStyle* extractedStyle = nullptr);
  inline bool shouldRemoveInlineStyleFromElement(EditingStyle* style,
                                                 HTMLElement* element) {
    return removeInlineStyleFromElement(style, element, ASSERT_NO_EDITING_ABORT,
                                        RemoveNone);
  }
  void replaceWithSpanOrRemoveIfWithoutAttributes(HTMLElement*, EditingState*);
  bool removeImplicitlyStyledElement(EditingStyle*,
                                     HTMLElement*,
                                     InlineStyleRemovalMode,
                                     EditingStyle* extractedStyle,
                                     EditingState*);
  bool removeCSSStyle(EditingStyle*,
                      HTMLElement*,
                      EditingState*,
                      InlineStyleRemovalMode = RemoveIfNeeded,
                      EditingStyle* extractedStyle = nullptr);
  HTMLElement* highestAncestorWithConflictingInlineStyle(EditingStyle*, Node*);
  void applyInlineStyleToPushDown(Node*, EditingStyle*, EditingState*);
  void pushDownInlineStyleAroundNode(EditingStyle*, Node*, EditingState*);
  void removeInlineStyle(EditingStyle*,
                         const Position& start,
                         const Position& end,
                         EditingState*);
  bool elementFullySelected(HTMLElement&,
                            const Position& start,
                            const Position& end) const;

  // style-application helpers
  void applyBlockStyle(EditingStyle*, EditingState*);
  void applyRelativeFontStyleChange(EditingStyle*, EditingState*);
  void applyInlineStyle(EditingStyle*, EditingState*);
  void fixRangeAndApplyInlineStyle(EditingStyle*,
                                   const Position& start,
                                   const Position& end,
                                   EditingState*);
  void applyInlineStyleToNodeRange(EditingStyle*,
                                   Node* startNode,
                                   Node* pastEndNode,
                                   EditingState*);
  void addBlockStyle(const StyleChange&, HTMLElement*);
  void addInlineStyleIfNeeded(EditingStyle*,
                              Node* start,
                              Node* end,
                              EditingState*);
  Position positionToComputeInlineStyleChange(
      Node*,
      Member<HTMLSpanElement>& dummyElement,
      EditingState*);
  void applyInlineStyleChange(Node* startNode,
                              Node* endNode,
                              StyleChange&,
                              EAddStyledElement,
                              EditingState*);
  void splitTextAtStart(const Position& start, const Position& end);
  void splitTextAtEnd(const Position& start, const Position& end);
  void splitTextElementAtStart(const Position& start, const Position& end);
  void splitTextElementAtEnd(const Position& start, const Position& end);
  bool shouldSplitTextElement(Element*, EditingStyle*);
  bool isValidCaretPositionInTextNode(const Position&);
  bool mergeStartWithPreviousIfIdentical(const Position& start,
                                         const Position& end,
                                         EditingState*);
  bool mergeEndWithNextIfIdentical(const Position& start,
                                   const Position& end,
                                   EditingState*);
  void cleanupUnstyledAppleStyleSpans(ContainerNode* dummySpanAncestor,
                                      EditingState*);

  void surroundNodeRangeWithElement(Node* start,
                                    Node* end,
                                    Element*,
                                    EditingState*);
  float computedFontSize(Node*);
  void joinChildTextNodes(ContainerNode*,
                          const Position& start,
                          const Position& end);

  HTMLElement* splitAncestorsWithUnicodeBidi(Node*,
                                             bool before,
                                             WritingDirection allowedDirection);
  void removeEmbeddingUpToEnclosingBlock(Node*,
                                         HTMLElement* unsplitAncestor,
                                         EditingState*);

  void updateStartEnd(const Position& newStart, const Position& newEnd);
  Position startPosition();
  Position endPosition();

  Member<EditingStyle> m_style;
  InputEvent::InputType m_inputType;
  EPropertyLevel m_propertyLevel;
  Position m_start;
  Position m_end;
  bool m_useEndingSelection;
  Member<Element> m_styledInlineElement;
  bool m_removeOnly;
  IsInlineElementToRemoveFunction m_isInlineElementToRemoveFunction;
};

enum ShouldStyleAttributeBeEmpty {
  AllowNonEmptyStyleAttribute,
  StyleAttributeShouldBeEmpty
};
bool isEmptyFontTag(const Element*,
                    ShouldStyleAttributeBeEmpty = StyleAttributeShouldBeEmpty);
bool isLegacyAppleHTMLSpanElement(const Node*);
bool isStyleSpanOrSpanWithOnlyStyleAttribute(const Element*);

}  // namespace blink

#endif


text:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>CSS Transitions: parsing transition-property with invalid values</title>
<link rel="help" href="https://drafts.csswg.org/css-transitions/#propdef-transition-property">
<meta name="assert" content="transition-property supports only the grammar 'none | <single-transition-property> #'.">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/css/support/parsing-testcommon.js"></script>
</head>
<body>
<script>
test_invalid_value("transition-property", 'one two three');
test_invalid_value("transition-property", '1, 2, 3');
test_invalid_value("transition-property", 'none, one');

test_invalid_value("transition-property", 'initial, top');
test_invalid_value("transition-property", 'top, initial');
</script>
</body>
</html>