ronantakizawa/codereview-bench huggingface dataset:
Repository: syncthing/syncthing
File: cmd/stdiscosrv/apisrv.go (Go)

Code before:
		hdr, err := base64.StdEncoding.DecodeString(hdr)
		if err != nil {
			// Decoding failed
			return nil, err
		}

		bs = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: hdr})
	} else if hdr := req.Header.Get("X-Forwarded-Tls-Client-Cert"); hdr != "" {
		// Traefik 2 passtlsclientcert
		// The certificate is in PEM format with url encoding but without newlines
		// and start/end statements. We need to decode, reinstate the newlines every 64
		// character and add statements for the PEM decoder
		hdr, err := url.QueryUnescape(hdr)
		if err != nil {
			// Decoding failed
			return nil, err
		}

		for i := 64; i < len(hdr); i += 65 {
			hdr = hdr[:i] + "\n" + hdr[i:]
		}

		hdr = "-----BEGIN CERTIFICATE-----\n" + hdr
		hdr = hdr + "\n-----END CERTIFICATE-----\n"
		bs = []byte(hdr)
	}

	if bs == nil {
		return nil, errors.New("empty certificate bytes")
	}

	block, _ := pem.Decode(bs)
	if block == nil {
		// Decoding failed
		return nil, errors.New("certificate decode results is empty")
	}

	return block.Bytes, nil
}

// fixupAddresses checks the list of addresses, removing invalid ones and
// replacing unspecified IPs with the given remote IP.
func fixupAddresses(remote *net.TCPAddr, addresses []string) []string {
	fixed := make([]string, 0, len(addresses))
	for _, annAddr := range addresses {
		uri, err := url.Parse(annAddr)
		if err != nil {
			continue
		}

		host, port, err := net.SplitHostPort(uri.Host)

Review comment (suggestion):
Could add an "else" here to return a more specific error, e.g. `no known certificate header present`.

Code after:
		hdr, err := base64.StdEncoding.DecodeString(hdr)
		if err != nil {
			// Decoding failed
			return nil, err
		}

		bs = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: hdr})
	} else if hdr := req.Header.Get("X-Forwarded-Tls-Client-Cert"); hdr != "" {
		// Traefik 2 passtlsclientcert
		// The certificate is in PEM format with url encoding but without newlines
		// and start/end statements. We need to decode, reinstate the newlines every 64
		// character and add statements for the PEM decoder
		hdr, err := url.QueryUnescape(hdr)
		if err != nil {
			// Decoding failed
			return nil, err
		}

		for i := 64; i < len(hdr); i += 65 {
			hdr = hdr[:i] + "\n" + hdr[i:]
		}

		hdr = "-----BEGIN CERTIFICATE-----\n" + hdr
		hdr = hdr + "\n-----END CERTIFICATE-----\n"
		bs = []byte(hdr)
	}

	if bs == nil {
		return nil, errors.New("empty certificate header")
	}

	block, _ := pem.Decode(bs)
	if block == nil {
		// Decoding failed
		return nil, errors.New("certificate decode result is empty")
	}

	return block.Bytes, nil
}

// fixupAddresses checks the list of addresses, removing invalid ones and
// replacing unspecified IPs with the given remote IP.
func fixupAddresses(remote *net.TCPAddr, addresses []string) []string {
	fixed := make([]string, 0, len(addresses))
	for _, annAddr := range addresses {
		uri, err := url.Parse(annAddr)
		if err != nil {
			continue
		}

		host, port, err := net.SplitHostPort(uri.Host)
Repository: syncthing/syncthing
File: cmd/stdiscosrv/apisrv.go (Go)

Code before:
		// The certificate is in PEM format with url encoding but without newlines
		// and start/end statements. We need to decode, reinstate the newlines every 64
		// character and add statements for the PEM decoder
		hdr, err := url.QueryUnescape(hdr)
		if err != nil {
			// Decoding failed
			return nil, err
		}

		for i := 64; i < len(hdr); i += 65 {
			hdr = hdr[:i] + "\n" + hdr[i:]
		}

		hdr = "-----BEGIN CERTIFICATE-----\n" + hdr
		hdr = hdr + "\n-----END CERTIFICATE-----\n"
		bs = []byte(hdr)
	}

	if bs == nil {
		return nil, errors.New("empty certificate bytes")
	}

	block, _ := pem.Decode(bs)
	if block == nil {
		// Decoding failed
		return nil, errors.New("certificate decode results is empty")
	}

	return block.Bytes, nil
}

// fixupAddresses checks the list of addresses, removing invalid ones and
// replacing unspecified IPs with the given remote IP.
func fixupAddresses(remote *net.TCPAddr, addresses []string) []string {
	fixed := make([]string, 0, len(addresses))
	for _, annAddr := range addresses {
		uri, err := url.Parse(annAddr)
		if err != nil {
			continue
		}

		host, port, err := net.SplitHostPort(uri.Host)
		if err != nil {
			continue
		}

		ip := net.ParseIP(host)

		// Some classes of IP are no-go.
		if ip.IsLoopback() || ip.IsMulticast() {
			continue

Review comment (suggestion):
```suggestion
		return nil, errors.New("certificate decode result is empty")
```

Code after:
		// The certificate is in PEM format with url encoding but without newlines
		// and start/end statements. We need to decode, reinstate the newlines every 64
		// character and add statements for the PEM decoder
		hdr, err := url.QueryUnescape(hdr)
		if err != nil {
			// Decoding failed
			return nil, err
		}

		for i := 64; i < len(hdr); i += 65 {
			hdr = hdr[:i] + "\n" + hdr[i:]
		}

		hdr = "-----BEGIN CERTIFICATE-----\n" + hdr
		hdr = hdr + "\n-----END CERTIFICATE-----\n"
		bs = []byte(hdr)
	}

	if bs == nil {
		return nil, errors.New("empty certificate header")
	}

	block, _ := pem.Decode(bs)
	if block == nil {
		// Decoding failed
		return nil, errors.New("certificate decode result is empty")
	}

	return block.Bytes, nil
}

// fixupAddresses checks the list of addresses, removing invalid ones and
// replacing unspecified IPs with the given remote IP.
func fixupAddresses(remote *net.TCPAddr, addresses []string) []string {
	fixed := make([]string, 0, len(addresses))
	for _, annAddr := range addresses {
		uri, err := url.Parse(annAddr)
		if err != nil {
			continue
		}

		host, port, err := net.SplitHostPort(uri.Host)
		if err != nil {
			continue
		}

		ip := net.ParseIP(host)

		// Some classes of IP are no-go.
		if ip.IsLoopback() || ip.IsMulticast() {
			continue
Repository: syncthing/syncthing
File: cmd/syncthing/cli/pending.go (Go)

Code before:
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.

package cli

import (
	"net/url"

	"github.com/urfave/cli"
)

var pendingCommand = cli.Command{
	Name:     "pending",
	HideHelp: true,
	Usage:    "Pending subcommand group",
	Subcommands: []cli.Command{
		{
			Name:   "devices",
			Usage:  "Show pending devices",
			Action: expects(0, indexDumpOutput("cluster/pending/devices")),
		},
		{
			Name:  "folders",
			Usage: "Show pending folders",
			Flags: []cli.Flag{
				cli.StringFlag{Name: "device", Usage: "Show pending folders for given device"},
			},
			Action: expects(0, folders()),
		},
	},
}

func folders() cli.ActionFunc {
	return func(c *cli.Context) error {
		if c.String("device") != "" {
			query := make(url.Values)
			query.Set("device", c.String("device"))
			return indexDumpOutput("cluster/pending/folders?" + query.Encode())(c)
		}
		return indexDumpOutput("cluster/pending/folders")(c)
	}
}


Review comment (suggestion):
```suggestion
				cli.StringFlag{Name: "device", Usage: "Show pending folders offered by given device"},
```

Code after:
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.

package cli

import (
	"net/url"

	"github.com/urfave/cli"
)

var pendingCommand = cli.Command{
	Name:     "pending",
	HideHelp: true,
	Usage:    "Pending subcommand group",
	Subcommands: []cli.Command{
		{
			Name:   "devices",
			Usage:  "Show pending devices",
			Action: expects(0, indexDumpOutput("cluster/pending/devices")),
		},
		{
			Name:  "folders",
			Usage: "Show pending folders",
			Flags: []cli.Flag{
				cli.StringFlag{Name: "device", Usage: "Show pending folders offered by given device"},
			},
			Action: expects(0, folders()),
		},
	},
}

func folders() cli.ActionFunc {
	return func(c *cli.Context) error {
		if c.String("device") != "" {
			query := make(url.Values)
			query.Set("device", c.String("device"))
			return indexDumpOutput("cluster/pending/folders?" + query.Encode())(c)
		}
		return indexDumpOutput("cluster/pending/folders")(c)
	}
}
Repository: syncthing/syncthing
File: lib/model/folderstate.go (Go)

Code before:
		return "idle"
	case FolderScanning:
		return "scanning"
	case FolderScanWaiting:
		return "scan-waiting"
	case FolderSyncWaiting:
		return "sync-waiting"
	case FolderSyncPreparing:
		return "sync-preparing"
	case FolderSyncing:
		return "syncing"
	case FolderCleaning:
		return "cleaning"
	case FolderCleanWaiting:
		return "clean-waiting"
	case FolderError:
		return "error"
	default:
		return "unknown"
	}
}

type remoteFolderState int

const (
	remoteMissing remoteFolderState = iota
	remotePaused
	remoteValid
)

type stateTracker struct {
	folderID string
	evLogger events.Logger

	mut     sync.Mutex
	current folderState
	err     error
	changed time.Time
}

func newStateTracker(id string, evLogger events.Logger) stateTracker {
	return stateTracker{
		folderID: id,
		evLogger: evLogger,
		mut:      sync.NewMutex(),
	}
}

// setState sets the new folder state, for states other than FolderError.
func (s *stateTracker) setState(newState folderState) {
	if newState == FolderError {

Review comment (suggestion):
Maybe `remoteNotSharing`? Without context I doubt it's clear what it means for a folder to be "missing".

Code after:
		return "idle"
	case FolderScanning:
		return "scanning"
	case FolderScanWaiting:
		return "scan-waiting"
	case FolderSyncWaiting:
		return "sync-waiting"
	case FolderSyncPreparing:
		return "sync-preparing"
	case FolderSyncing:
		return "syncing"
	case FolderCleaning:
		return "cleaning"
	case FolderCleanWaiting:
		return "clean-waiting"
	case FolderError:
		return "error"
	default:
		return "unknown"
	}
}

type remoteFolderState int

const (
	remoteNotSharing remoteFolderState = iota
	remotePaused
	remoteValid
)

type stateTracker struct {
	folderID string
	evLogger events.Logger

	mut     sync.Mutex
	current folderState
	err     error
	changed time.Time
}

func newStateTracker(id string, evLogger events.Logger) stateTracker {
	return stateTracker{
		folderID: id,
		evLogger: evLogger,
		mut:      sync.NewMutex(),
	}
}

// setState sets the new folder state, for states other than FolderError.
func (s *stateTracker) setState(newState folderState) {
	if newState == FolderError {
Repository: syncthing/syncthing
File: lib/fs/filesystem.go (Go)

Code before:
			i++
		}
	}

	var fs Filesystem
	switch fsType {
	case FilesystemTypeBasic:
		fs = newBasicFilesystem(uri, opts...)
	case FilesystemTypeFake:
		fs = newFakeFilesystem(uri, opts...)
	default:
		l.Debugln("Unknown filesystem", fsType, uri)
		fs = &errorFilesystem{
			fsType: fsType,
			uri:    uri,
			err:    errors.New("filesystem with type " + fsType.String() + " does not exist."),
		}
	}

	// Case handling is the innermost, as any filesystem calls by wrappers should be case-resolved
	if caseOpt != nil {
		fs = caseOpt.apply(fs)
	}

	// mtime handling should happen inside walking, as filesystem calls while
	// walking should be mtime-resoved too
	if mtimeOpt != nil {
		fs = mtimeOpt.apply(fs)
	}

	if l.ShouldDebug("walkfs") {
		return NewWalkFilesystem(&logFilesystem{fs})
	}

	if l.ShouldDebug("fs") {
		return &logFilesystem{NewWalkFilesystem(fs)}
	}

	return NewWalkFilesystem(fs)
}

// IsInternal returns true if the file, as a path relative to the folder
// root, represents an internal file that should always be ignored. The file
// path must be clean (i.e., in canonical shortest form).
func IsInternal(file string) bool {
	// fs cannot import config, so we hard code .stfolder here (config.DefaultMarkerName)
	internals := []string{".stfolder", ".stignore", ".stversions"}
	for _, internal := range internals {
		if file == internal {
			return true
		}

Review comment (suggestion):
```suggestion
	// walking should be mtime-resolved too
```

Code after:
			i++
		}
	}
	opts = opts[:i]

	var fs Filesystem
	switch fsType {
	case FilesystemTypeBasic:
		fs = newBasicFilesystem(uri, opts...)
	case FilesystemTypeFake:
		fs = newFakeFilesystem(uri, opts...)
	default:
		l.Debugln("Unknown filesystem", fsType, uri)
		fs = &errorFilesystem{
			fsType: fsType,
			uri:    uri,
			err:    errors.New("filesystem with type " + fsType.String() + " does not exist."),
		}
	}

	// Case handling is the innermost, as any filesystem calls by wrappers should be case-resolved
	if caseOpt != nil {
		fs = caseOpt.apply(fs)
	}

	// mtime handling should happen inside walking, as filesystem calls while
	// walking should be mtime-resolved too
	if mtimeOpt != nil {
		fs = mtimeOpt.apply(fs)
	}

	if l.ShouldDebug("walkfs") {
		return NewWalkFilesystem(&logFilesystem{fs})
	}

	if l.ShouldDebug("fs") {
		return &logFilesystem{NewWalkFilesystem(fs)}
	}

	return NewWalkFilesystem(fs)
}

// IsInternal returns true if the file, as a path relative to the folder
// root, represents an internal file that should always be ignored. The file
// path must be clean (i.e., in canonical shortest form).
func IsInternal(file string) bool {
	// fs cannot import config, so we hard code .stfolder here (config.DefaultMarkerName)
	internals := []string{".stfolder", ".stignore", ".stversions"}
	for _, internal := range internals {
		if file == internal {
			return true
Repository: syncthing/syncthing
File: lib/model/folderstate.go (Go)

Code before:
	case FolderScanWaiting:
		return "scan-waiting"
	case FolderSyncWaiting:
		return "sync-waiting"
	case FolderSyncPreparing:
		return "sync-preparing"
	case FolderSyncing:
		return "syncing"
	case FolderCleaning:
		return "cleaning"
	case FolderCleanWaiting:
		return "clean-waiting"
	case FolderError:
		return "error"
	default:
		return "unknown"
	}
}

type remoteFolderState int

const (
	remoteUnknown remoteFolderState = iota
	remoteNotSharing
	remotePaused
	remoteValid
)

func (s remoteFolderState) String() string {
	switch s {
	case remoteUnknown:
		return "unknown"
	case remoteNotSharing:
		return "notsharing"
	case remotePaused:
		return "paused"
	case remoteValid:
		return "valid"
	default:
		return "unknown"
	}
}

type stateTracker struct {
	folderID string
	evLogger events.Logger

	mut     sync.Mutex
	current folderState
	err     error
	changed time.Time

Review comment (suggestion):
Didn't notice before, but given these are global variables they should have the full `remoteFolder` prefix: `remotePaused` might as well mean "the remote device is paused".

Code after:
	case FolderScanWaiting:
		return "scan-waiting"
	case FolderSyncWaiting:
		return "sync-waiting"
	case FolderSyncPreparing:
		return "sync-preparing"
	case FolderSyncing:
		return "syncing"
	case FolderCleaning:
		return "cleaning"
	case FolderCleanWaiting:
		return "clean-waiting"
	case FolderError:
		return "error"
	default:
		return "unknown"
	}
}

type remoteFolderState int

const (
	remoteFolderUnknown remoteFolderState = iota
	remoteFolderNotSharing
	remoteFolderPaused
	remoteFolderValid
)

func (s remoteFolderState) String() string {
	switch s {
	case remoteFolderUnknown:
		return "unknown"
	case remoteFolderNotSharing:
		return "notSharing"
	case remoteFolderPaused:
		return "paused"
	case remoteFolderValid:
		return "valid"
	default:
		return "unknown"
	}
}

func (s remoteFolderState) MarshalText() ([]byte, error) {
	return []byte(s.String()), nil
}

type stateTracker struct {
	folderID string
	evLogger events.Logger
Repository: syncthing/syncthing
File: lib/model/folderstate.go (Go)

Code before:
	case FolderCleaning:
		return "cleaning"
	case FolderCleanWaiting:
		return "clean-waiting"
	case FolderError:
		return "error"
	default:
		return "unknown"
	}
}

type remoteFolderState int

const (
	remoteUnknown remoteFolderState = iota
	remoteNotSharing
	remotePaused
	remoteValid
)

func (s remoteFolderState) String() string {
	switch s {
	case remoteUnknown:
		return "unknown"
	case remoteNotSharing:
		return "notsharing"
	case remotePaused:
		return "paused"
	case remoteValid:
		return "valid"
	default:
		return "unknown"
	}
}

type stateTracker struct {
	folderID string
	evLogger events.Logger

	mut     sync.Mutex
	current folderState
	err     error
	changed time.Time
}

func newStateTracker(id string, evLogger events.Logger) stateTracker {
	return stateTracker{
		folderID: id,
		evLogger: evLogger,
		mut:      sync.NewMutex(),
	}

Review comment (suggestion):
Just wanted to say we usually use camelCase, but checking it we do mix various cases for both keys and values in string and json representations (found all of lowercase, kebab-case and camelCase). Personally I'd prefer camelCase.

Code after:
	case FolderCleaning:
		return "cleaning"
	case FolderCleanWaiting:
		return "clean-waiting"
	case FolderError:
		return "error"
	default:
		return "unknown"
	}
}

type remoteFolderState int

const (
	remoteFolderUnknown remoteFolderState = iota
	remoteFolderNotSharing
	remoteFolderPaused
	remoteFolderValid
)

func (s remoteFolderState) String() string {
	switch s {
	case remoteFolderUnknown:
		return "unknown"
	case remoteFolderNotSharing:
		return "notSharing"
	case remoteFolderPaused:
		return "paused"
	case remoteFolderValid:
		return "valid"
	default:
		return "unknown"
	}
}

func (s remoteFolderState) MarshalText() ([]byte, error) {
	return []byte(s.String()), nil
}

type stateTracker struct {
	folderID string
	evLogger events.Logger

	mut     sync.Mutex
	current folderState
	err     error
	changed time.Time
}

func newStateTracker(id string, evLogger events.Logger) stateTracker {
	return stateTracker{
Repository: syncthing/syncthing
File: lib/events/events.go (Go)

Code before:
import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"runtime"
	"time"

	"github.com/thejerf/suture/v4"

	"github.com/syncthing/syncthing/lib/sync"
)

type EventType int64

const (
	Starting EventType = 1 << iota
	StartupComplete
	DeviceDiscovered
	DeviceConnected
	DeviceDisconnected
	DeviceRejected // DEPRECATED, superseded by PendingDevicesChanged
	PendingDevicesChanged
	DevicePaused
	DeviceResumed
	RemoteClusterConfigReceived
	LocalChangeDetected
	RemoteChangeDetected
	LocalIndexUpdated
	RemoteIndexUpdated
	ItemStarted
	ItemFinished
	StateChanged
	FolderRejected // DEPRECATED, superseded by PendingFoldersChanged
	PendingFoldersChanged
	ConfigSaved
	DownloadProgress
	RemoteDownloadProgress
	FolderSummary
	FolderCompletion
	FolderErrors
	FolderScanProgress
	FolderPaused
	FolderResumed
	FolderWatchStateChanged
	ListenAddressesChanged
	LoginAttempt
	Failure

	AllEvents = (1 << iota) - 1
)

Review comment (suggestion):
@calmh Proposed `RemoteFoldersChanged`. I like that because the "cluster-config" term ins't particularly nice/descriptive, but mirroring the BEP message name has the benefit of making that connection. I am undecided on this.

Code after:
import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"runtime"
	"time"

	"github.com/thejerf/suture/v4"

	"github.com/syncthing/syncthing/lib/sync"
)

type EventType int64

const (
	Starting EventType = 1 << iota
	StartupComplete
	DeviceDiscovered
	DeviceConnected
	DeviceDisconnected
	DeviceRejected // DEPRECATED, superseded by PendingDevicesChanged
	PendingDevicesChanged
	DevicePaused
	DeviceResumed
	ClusterConfigReceived
	LocalChangeDetected
	RemoteChangeDetected
	LocalIndexUpdated
	RemoteIndexUpdated
	ItemStarted
	ItemFinished
	StateChanged
	FolderRejected // DEPRECATED, superseded by PendingFoldersChanged
	PendingFoldersChanged
	ConfigSaved
	DownloadProgress
	RemoteDownloadProgress
	FolderSummary
	FolderCompletion
	FolderErrors
	FolderScanProgress
	FolderPaused
	FolderResumed
	FolderWatchStateChanged
	ListenAddressesChanged
	LoginAttempt
	Failure

	AllEvents = (1 << iota) - 1
)
Repository: syncthing/syncthing
File: lib/model/model.go (Go)

Code before:
}

// FolderStatistics returns statistics about each folder
func (m *model) FolderStatistics() (map[string]stats.FolderStatistics, error) {
	res := make(map[string]stats.FolderStatistics)
	m.fmut.RLock()
	defer m.fmut.RUnlock()
	for id, runner := range m.folderRunners {
		stats, err := runner.GetStatistics()
		if err != nil {
			return nil, err
		}
		res[id] = stats
	}
	return res, nil
}

type FolderCompletion struct {
	CompletionPct float64
	GlobalBytes   int64
	NeedBytes     int64
	GlobalItems   int
	NeedItems     int
	NeedDeletes   int
	Sequence      int64
	RemoteState   string
}

func newFolderCompletion(global, need db.Counts, sequence int64, state remoteFolderState) FolderCompletion {
	comp := FolderCompletion{
		GlobalBytes: global.Bytes,
		NeedBytes:   need.Bytes,
		GlobalItems: global.Files + global.Directories + global.Symlinks,
		NeedItems:   need.Files + need.Directories + need.Symlinks,
		NeedDeletes: need.Deleted,
		Sequence:    sequence,
		RemoteState: state.String(),
	}
	comp.setComplectionPct()
	return comp
}

func (comp *FolderCompletion) add(other FolderCompletion) {
	comp.GlobalBytes += other.GlobalBytes
	comp.NeedBytes += other.NeedBytes
	comp.GlobalItems += other.GlobalItems
	comp.NeedItems += other.NeedItems
	comp.NeedDeletes += other.NeedDeletes
	comp.setComplectionPct()
}


Review comment (suggestion):
I think there's some value in keeping this as a `remoteFolderState` here still. Currently it's only used in the api, where the conversion to string is needed, but say some service listens to the events and wants to act on certain states -> the typed values are nicer to use there.

Code after:
}

// FolderStatistics returns statistics about each folder
func (m *model) FolderStatistics() (map[string]stats.FolderStatistics, error) {
	res := make(map[string]stats.FolderStatistics)
	m.fmut.RLock()
	defer m.fmut.RUnlock()
	for id, runner := range m.folderRunners {
		stats, err := runner.GetStatistics()
		if err != nil {
			return nil, err
		}
		res[id] = stats
	}
	return res, nil
}

type FolderCompletion struct {
	CompletionPct float64
	GlobalBytes   int64
	NeedBytes     int64
	GlobalItems   int
	NeedItems     int
	NeedDeletes   int
	Sequence      int64
	RemoteState   remoteFolderState
}

func newFolderCompletion(global, need db.Counts, sequence int64, state remoteFolderState) FolderCompletion {
	comp := FolderCompletion{
		GlobalBytes: global.Bytes,
		NeedBytes:   need.Bytes,
		GlobalItems: global.Files + global.Directories + global.Symlinks,
		NeedItems:   need.Files + need.Directories + need.Symlinks,
		NeedDeletes: need.Deleted,
		Sequence:    sequence,
		RemoteState: state,
	}
	comp.setComplectionPct()
	return comp
}

func (comp *FolderCompletion) add(other FolderCompletion) {
	comp.GlobalBytes += other.GlobalBytes
	comp.NeedBytes += other.NeedBytes
	comp.GlobalItems += other.GlobalItems
	comp.NeedItems += other.NeedItems
	comp.NeedDeletes += other.NeedDeletes
	comp.setComplectionPct()
}