summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/podman/common/specgen.go4
-rw-r--r--cmd/podman/inspect/inspect.go56
-rw-r--r--docs/source/markdown/podman-create.1.md1
-rw-r--r--docs/source/markdown/podman-run.1.md9
-rw-r--r--go.mod2
-rw-r--r--go.sum10
-rw-r--r--libpod/container.go4
-rw-r--r--libpod/container_log.go23
-rw-r--r--libpod/options.go2
-rw-r--r--pkg/api/handlers/libpod/containers_create.go5
-rw-r--r--pkg/domain/entities/engine_container.go2
-rw-r--r--pkg/domain/entities/engine_image.go2
-rw-r--r--pkg/domain/entities/volumes.go6
-rw-r--r--pkg/domain/infra/abi/containers.go65
-rw-r--r--pkg/domain/infra/abi/images.go14
-rw-r--r--pkg/domain/infra/tunnel/containers.go35
-rw-r--r--pkg/domain/infra/tunnel/images.go15
-rw-r--r--pkg/hooks/0.1.0/hook.go9
-rw-r--r--pkg/hooks/0.1.0/hook_test.go26
-rw-r--r--pkg/hooks/read.go6
-rw-r--r--pkg/specgen/generate/container.go159
-rw-r--r--pkg/specgen/generate/container_create.go8
-rw-r--r--pkg/specgen/generate/validate.go159
-rw-r--r--pkg/systemd/generate/containers.go3
-rw-r--r--pkg/systemd/generate/containers_test.go3
-rw-r--r--pkg/systemd/generate/pods.go3
-rw-r--r--pkg/systemd/generate/pods_test.go1
-rw-r--r--test/e2e/create_test.go14
-rw-r--r--test/e2e/inspect_test.go40
-rw-r--r--test/e2e/logs_test.go32
-rw-r--r--vendor/github.com/containers/image/v5/copy/copy.go14
-rw-r--r--vendor/github.com/containers/image/v5/oci/layout/oci_dest.go2
-rw-r--r--vendor/github.com/containers/image/v5/types/types.go4
-rw-r--r--vendor/github.com/containers/image/v5/version/version.go4
-rw-r--r--vendor/github.com/klauspost/compress/fse/bitreader.go27
-rw-r--r--vendor/github.com/klauspost/compress/fse/bytereader.go13
-rw-r--r--vendor/github.com/klauspost/compress/huff0/README.md6
-rw-r--r--vendor/github.com/klauspost/compress/huff0/bitreader.go256
-rw-r--r--vendor/github.com/klauspost/compress/huff0/decompress.go776
-rw-r--r--vendor/github.com/klauspost/compress/zstd/bitreader.go25
-rw-r--r--vendor/github.com/klauspost/compress/zstd/blockdec.go4
-rw-r--r--vendor/github.com/klauspost/compress/zstd/bytereader.go25
-rw-r--r--vendor/github.com/klauspost/compress/zstd/decoder.go15
-rw-r--r--vendor/github.com/klauspost/compress/zstd/fse_decoder.go19
-rw-r--r--vendor/modules.txt4
45 files changed, 1555 insertions, 357 deletions
diff --git a/cmd/podman/common/specgen.go b/cmd/podman/common/specgen.go
index c8d9888b7..e6a524358 100644
--- a/cmd/podman/common/specgen.go
+++ b/cmd/podman/common/specgen.go
@@ -627,7 +627,7 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string
if retries < 0 {
return errors.Errorf("must specify restart policy retry count as a number greater than 0")
}
- var retriesUint uint = uint(retries)
+ var retriesUint = uint(retries)
s.RestartRetries = &retriesUint
default:
return errors.Errorf("invalid restart policy: may specify retries at most once")
@@ -637,8 +637,6 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string
s.Remove = c.Rm
s.StopTimeout = &c.StopTimeout
- // TODO where should we do this?
- // func verifyContainerResources(config *cc.CreateConfig, update bool) ([]string, error) {
return nil
}
diff --git a/cmd/podman/inspect/inspect.go b/cmd/podman/inspect/inspect.go
index 1ed033ec3..d80bbffdd 100644
--- a/cmd/podman/inspect/inspect.go
+++ b/cmd/podman/inspect/inspect.go
@@ -3,12 +3,14 @@ package inspect
import (
"context"
"fmt"
+ "os"
"strings"
"github.com/containers/buildah/pkg/formats"
"github.com/containers/libpod/cmd/podman/registry"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
@@ -78,6 +80,7 @@ func newInspector(options entities.InspectOptions) (*inspector, error) {
func (i *inspector) inspect(namesOrIDs []string) error {
// data - dumping place for inspection results.
var data []interface{} //nolint
+ var errs []error
ctx := context.Background()
if len(namesOrIDs) == 0 {
@@ -97,24 +100,27 @@ func (i *inspector) inspect(namesOrIDs []string) error {
// Inspect - note that AllType requires us to expensively query one-by-one.
switch tmpType {
case AllType:
- all, err := i.inspectAll(ctx, namesOrIDs)
+ allData, allErrs, err := i.inspectAll(ctx, namesOrIDs)
if err != nil {
return err
}
- data = all
+ data = allData
+ errs = allErrs
case ImageType:
- imgData, err := i.imageEngine.Inspect(ctx, namesOrIDs, i.options)
+ imgData, allErrs, err := i.imageEngine.Inspect(ctx, namesOrIDs, i.options)
if err != nil {
return err
}
+ errs = allErrs
for i := range imgData {
data = append(data, imgData[i])
}
case ContainerType:
- ctrData, err := i.containerEngine.ContainerInspect(ctx, namesOrIDs, i.options)
+ ctrData, allErrs, err := i.containerEngine.ContainerInspect(ctx, namesOrIDs, i.options)
if err != nil {
return err
}
+ errs = allErrs
for i := range ctrData {
data = append(data, ctrData[i])
}
@@ -122,30 +128,54 @@ func (i *inspector) inspect(namesOrIDs []string) error {
return errors.Errorf("invalid type %q: must be %q, %q or %q", i.options.Type, ImageType, ContainerType, AllType)
}
+ // Always print an empty array
+ if data == nil {
+ data = []interface{}{}
+ }
+
var out formats.Writer
if i.options.Format == "json" || i.options.Format == "" { // "" for backwards compat
out = formats.JSONStructArray{Output: data}
} else {
out = formats.StdoutTemplateArray{Output: data, Template: inspectFormat(i.options.Format)}
}
- return out.Out()
+ if err := out.Out(); err != nil {
+ logrus.Errorf("Error printing inspect output: %v", err)
+ }
+ if len(errs) > 0 {
+ if len(errs) > 1 {
+ for _, err := range errs[1:] {
+ fmt.Fprintf(os.Stderr, "error inspecting object: %v\n", err)
+ }
+ }
+ return errors.Errorf("error inspecting object: %v", errs[0])
+ }
+ return nil
}
-func (i *inspector) inspectAll(ctx context.Context, namesOrIDs []string) ([]interface{}, error) {
+func (i *inspector) inspectAll(ctx context.Context, namesOrIDs []string) ([]interface{}, []error, error) {
var data []interface{} //nolint
+ allErrs := []error{}
for _, name := range namesOrIDs {
- imgData, err := i.imageEngine.Inspect(ctx, []string{name}, i.options)
- if err == nil {
- data = append(data, imgData[0])
+ ctrData, errs, err := i.containerEngine.ContainerInspect(ctx, []string{name}, i.options)
+ if err != nil {
+ return nil, nil, err
+ }
+ if len(errs) == 0 {
+ data = append(data, ctrData[0])
continue
}
- ctrData, err := i.containerEngine.ContainerInspect(ctx, []string{name}, i.options)
+ imgData, errs, err := i.imageEngine.Inspect(ctx, []string{name}, i.options)
if err != nil {
- return nil, err
+ return nil, nil, err
+ }
+ if len(errs) > 0 {
+ allErrs = append(allErrs, errors.Errorf("no such object: %q", name))
+ continue
}
- data = append(data, ctrData[0])
+ data = append(data, imgData[0])
}
- return data, nil
+ return data, allErrs, nil
}
func inspectFormat(row string) string {
diff --git a/docs/source/markdown/podman-create.1.md b/docs/source/markdown/podman-create.1.md
index 1da9d72e6..03ac8642f 100644
--- a/docs/source/markdown/podman-create.1.md
+++ b/docs/source/markdown/podman-create.1.md
@@ -676,6 +676,7 @@ Valid values are:
- `no` : Do not restart containers on exit
- `on-failure[:max_retries]` : Restart containers when they exit with a non-0 exit code, retrying indefinitely or until the optional max_retries count is hit
- `always` : Restart containers when they exit, regardless of status, retrying indefinitely
+- `unless-stopped` : Identical to **always**
Please note that restart will not restart containers after a system reboot.
If this functionality is required in your environment, you can invoke Podman from a systemd unit file, or create an init script for whichever init system is in use.
diff --git a/docs/source/markdown/podman-run.1.md b/docs/source/markdown/podman-run.1.md
index 3e1ade047..539e62819 100644
--- a/docs/source/markdown/podman-run.1.md
+++ b/docs/source/markdown/podman-run.1.md
@@ -682,11 +682,10 @@ Restart policy will not take effect if a container is stopped via the **podman k
Valid _policy_ values are:
-- **no**: Do not restart containers on exit;
-- **on-failure**[:*max_retries*]: Restart containers when they exit
-with a non-zero exit code, retrying indefinitely or until the optional
-*max_retries* count is hit;
-- **always**: Restart containers when they exit, regardless of status, retrying indefinitely.
+- `no` : Do not restart containers on exit
+- `on-failure[:max_retries]` : Restart containers when they exit with a non-zero exit code, retrying indefinitely or until the optional *max_retries* count is hit
+- `always` : Restart containers when they exit, regardless of status, retrying indefinitely
+- `unless-stopped` : Identical to **always**
Please note that restart will not restart containers after a system reboot.
If this functionality is required in your environment, you can invoke Podman from a **systemd.unit**(5) file, or create an init script for whichever init system is in use.
diff --git a/go.mod b/go.mod
index e5cbb1073..9a08a0ef2 100644
--- a/go.mod
+++ b/go.mod
@@ -13,7 +13,7 @@ require (
github.com/containers/buildah v1.14.9-0.20200523094741-de0f541d9224
github.com/containers/common v0.14.0
github.com/containers/conmon v2.0.18+incompatible
- github.com/containers/image/v5 v5.4.5-0.20200529084758-46b2ee6aebb0
+ github.com/containers/image/v5 v5.5.1
github.com/containers/psgo v1.5.1
github.com/containers/storage v1.20.2
github.com/coreos/go-systemd/v22 v22.1.0
diff --git a/go.sum b/go.sum
index e5e65df1d..55c5fa6db 100644
--- a/go.sum
+++ b/go.sum
@@ -76,8 +76,8 @@ github.com/containers/conmon v2.0.18+incompatible h1:rjwjNnE756NuXcdE/uUmj4kDbry
github.com/containers/conmon v2.0.18+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I=
github.com/containers/image/v5 v5.4.3/go.mod h1:pN0tvp3YbDd7BWavK2aE0mvJUqVd2HmhPjekyWSFm0U=
github.com/containers/image/v5 v5.4.4/go.mod h1:g7cxNXitiLi6pEr9/L9n/0wfazRuhDKXU15kV86N8h8=
-github.com/containers/image/v5 v5.4.5-0.20200529084758-46b2ee6aebb0 h1:K1ez+qAi9hCMHv/akPF4ddZumQTq/PBGf2Nzc7e+7lI=
-github.com/containers/image/v5 v5.4.5-0.20200529084758-46b2ee6aebb0/go.mod h1:XRf/UlTCkDBQONudBhSFXiLCouFKPU/oXwIjWw/tPpo=
+github.com/containers/image/v5 v5.5.1 h1:h1FCOXH6Ux9/p/E4rndsQOC4yAdRU0msRTfLVeQ7FDQ=
+github.com/containers/image/v5 v5.5.1/go.mod h1:4PyNYR0nwlGq/ybVJD9hWlhmIsNra4Q8uOQX2s6E2uM=
github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b h1:Q8ePgVfHDplZ7U33NwHZkrVELsZP5fYj9pM5WBZB2GE=
github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY=
github.com/containers/ocicrypt v1.0.2 h1:Q0/IPs8ohfbXNxEfyJ2pFVmvJu5BhqJUAmc6ES9NKbo=
@@ -87,8 +87,6 @@ github.com/containers/psgo v1.5.1/go.mod h1:2ubh0SsreMZjSXW1Hif58JrEcFudQyIy9EzP
github.com/containers/storage v1.18.2/go.mod h1:WTBMf+a9ZZ/LbmEVeLHH2TX4CikWbO1Bt+/m58ZHVPg=
github.com/containers/storage v1.19.1/go.mod h1:KbXjSwKnx17ejOsjFcCXSf78mCgZkQSLPBNTMRc3XrQ=
github.com/containers/storage v1.19.2/go.mod h1:gYCp3jzgXkvubO0rI14QAjz5Mxm/qKJgLmHFyqayDnw=
-github.com/containers/storage v1.20.1 h1:2XE4eRIqSa6YjhAZjNwIkIKE6+Miy+5WV8l1KzY2ZKk=
-github.com/containers/storage v1.20.1/go.mod h1:RoKzO8KSDogCT6c06rEbanZTcKYxshorB33JikEGc3A=
github.com/containers/storage v1.20.2 h1:tw/uKRPDnmVrluIzer3dawTFG/bTJLP8IEUyHFhltYk=
github.com/containers/storage v1.20.2/go.mod h1:oOB9Ie8OVPojvoaKWEGSEtHbXUAs+tSyr7RO7ZGteMc=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
@@ -257,10 +255,10 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.10.5 h1:7q6vHIqubShURwQz8cQK6yIe/xC3IF0Vm7TGfqjewrc=
github.com/klauspost/compress v1.10.5/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
-github.com/klauspost/compress v1.10.6 h1:SP6zavvTG3YjOosWePXFDlExpKIWMTO4SE/Y8MZB2vI=
-github.com/klauspost/compress v1.10.6/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.10.7 h1:7rix8v8GpI3ZBb0nSozFRgbtXKv+hOe+qfEpZqybrAg=
github.com/klauspost/compress v1.10.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
+github.com/klauspost/compress v1.10.8 h1:eLeJ3dr/Y9+XRfJT4l+8ZjmtB5RPJhucH2HeCV5+IZY=
+github.com/klauspost/compress v1.10.8/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/pgzip v1.2.3/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/klauspost/pgzip v1.2.4 h1:TQ7CNpYKovDOmqzRHKxJh0BeaBI7UdQZYc6p7pMQh1A=
github.com/klauspost/pgzip v1.2.4/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
diff --git a/libpod/container.go b/libpod/container.go
index d4a779b13..20702903e 100644
--- a/libpod/container.go
+++ b/libpod/container.go
@@ -97,6 +97,10 @@ const (
// RestartPolicyOnFailure restarts the container on non-0 exit code,
// with an optional maximum number of retries.
RestartPolicyOnFailure = "on-failure"
+ // RestartPolicyUnlessStopped unconditionally restarts unless stopped
+ // by the user. It is identical to Always except with respect to
+ // handling of system restart, which Podman does not yet support.
+ RestartPolicyUnlessStopped = "unless-stopped"
)
// Container is a single OCI container.
diff --git a/libpod/container_log.go b/libpod/container_log.go
index 39c395fe6..c3a84d048 100644
--- a/libpod/container_log.go
+++ b/libpod/container_log.go
@@ -2,7 +2,6 @@ package libpod
import (
"os"
- "time"
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/libpod/logs"
@@ -73,27 +72,5 @@ func (c *Container) readFromLogFile(options *logs.LogOptions, logChannel chan *l
}
options.WaitGroup.Done()
}()
- // Check if container is still running or paused
- go func() {
- if options.Follow {
- for {
- state, err := c.State()
- if err != nil && errors.Cause(err) != define.ErrNoSuchCtr {
- logrus.Error(err)
- break
- } else if err != nil {
- break
- }
- if state != define.ContainerStateRunning && state != define.ContainerStatePaused {
- err := t.Stop()
- if err != nil {
- logrus.Error(err)
- }
- break
- }
- time.Sleep(1 * time.Second)
- }
- }
- }()
return nil
}
diff --git a/libpod/options.go b/libpod/options.go
index 5a0f60093..e1c4ddf06 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -1285,7 +1285,7 @@ func WithRestartPolicy(policy string) CtrCreateOption {
}
switch policy {
- case RestartPolicyNone, RestartPolicyNo, RestartPolicyOnFailure, RestartPolicyAlways:
+ case RestartPolicyNone, RestartPolicyNo, RestartPolicyOnFailure, RestartPolicyAlways, RestartPolicyUnlessStopped:
ctr.config.RestartPolicy = policy
default:
return errors.Wrapf(define.ErrInvalidArg, "%q is not a valid restart policy", policy)
diff --git a/pkg/api/handlers/libpod/containers_create.go b/pkg/api/handlers/libpod/containers_create.go
index 71f440bce..8fbff9be7 100644
--- a/pkg/api/handlers/libpod/containers_create.go
+++ b/pkg/api/handlers/libpod/containers_create.go
@@ -22,7 +22,8 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()"))
return
}
- if err := generate.CompleteSpec(r.Context(), runtime, &sg); err != nil {
+ warn, err := generate.CompleteSpec(r.Context(), runtime, &sg)
+ if err != nil {
utils.InternalServerError(w, err)
return
}
@@ -31,6 +32,6 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) {
utils.InternalServerError(w, err)
return
}
- response := entities.ContainerCreateResponse{ID: ctr.ID()}
+ response := entities.ContainerCreateResponse{ID: ctr.ID(), Warnings: warn}
utils.WriteJSON(w, http.StatusCreated, response)
}
diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go
index 979df7581..837550a2e 100644
--- a/pkg/domain/entities/engine_container.go
+++ b/pkg/domain/entities/engine_container.go
@@ -24,7 +24,7 @@ type ContainerEngine interface {
ContainerExists(ctx context.Context, nameOrID string) (*BoolReport, error)
ContainerExport(ctx context.Context, nameOrID string, options ContainerExportOptions) error
ContainerInit(ctx context.Context, namesOrIds []string, options ContainerInitOptions) ([]*ContainerInitReport, error)
- ContainerInspect(ctx context.Context, namesOrIds []string, options InspectOptions) ([]*ContainerInspectReport, error)
+ ContainerInspect(ctx context.Context, namesOrIds []string, options InspectOptions) ([]*ContainerInspectReport, []error, error)
ContainerKill(ctx context.Context, namesOrIds []string, options KillOptions) ([]*KillReport, error)
ContainerList(ctx context.Context, options ContainerListOptions) ([]ListContainer, error)
ContainerLogs(ctx context.Context, containers []string, options ContainerLogsOptions) error
diff --git a/pkg/domain/entities/engine_image.go b/pkg/domain/entities/engine_image.go
index 60fb20b6e..7ece24c60 100644
--- a/pkg/domain/entities/engine_image.go
+++ b/pkg/domain/entities/engine_image.go
@@ -13,7 +13,7 @@ type ImageEngine interface {
Exists(ctx context.Context, nameOrID string) (*BoolReport, error)
History(ctx context.Context, nameOrID string, opts ImageHistoryOptions) (*ImageHistoryReport, error)
Import(ctx context.Context, opts ImageImportOptions) (*ImageImportReport, error)
- Inspect(ctx context.Context, namesOrIDs []string, opts InspectOptions) ([]*ImageInspectReport, error)
+ Inspect(ctx context.Context, namesOrIDs []string, opts InspectOptions) ([]*ImageInspectReport, []error, error)
List(ctx context.Context, opts ImageListOptions) ([]*ImageSummary, error)
Load(ctx context.Context, opts ImageLoadOptions) (*ImageLoadReport, error)
Prune(ctx context.Context, opts ImagePruneOptions) (*ImagePruneReport, error)
diff --git a/pkg/domain/entities/volumes.go b/pkg/domain/entities/volumes.go
index 7cf7d82a2..c99b39f2d 100644
--- a/pkg/domain/entities/volumes.go
+++ b/pkg/domain/entities/volumes.go
@@ -47,13 +47,13 @@ type VolumeConfigResponse struct {
// It is presently not used.
Options map[string]string `json:"Options"`
// UID is the UID that the volume was created with.
- UID int `json:"UID,omitempty"`
+ UID int `json:"UID"`
// GID is the GID that the volume was created with.
- GID int `json:"GID,omitempty"`
+ GID int `json:"GID"`
// Anonymous indicates that the volume was created as an anonymous
// volume for a specific container, and will be be removed when any
// container using it is removed.
- Anonymous bool `json:"Anonymous,omitempty"`
+ Anonymous bool `json:"Anonymous"`
}
type VolumeRmOptions struct {
diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go
index d2c8aefdc..8e0ffc075 100644
--- a/pkg/domain/infra/abi/containers.go
+++ b/pkg/domain/infra/abi/containers.go
@@ -338,20 +338,51 @@ func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string,
return reports, nil
}
-func (ic *ContainerEngine) ContainerInspect(ctx context.Context, namesOrIds []string, options entities.InspectOptions) ([]*entities.ContainerInspectReport, error) {
- ctrs, err := getContainersByContext(false, options.Latest, namesOrIds, ic.Libpod)
- if err != nil {
- return nil, err
+func (ic *ContainerEngine) ContainerInspect(ctx context.Context, namesOrIds []string, options entities.InspectOptions) ([]*entities.ContainerInspectReport, []error, error) {
+ if options.Latest {
+ ctr, err := ic.Libpod.GetLatestContainer()
+ if err != nil {
+ if errors.Cause(err) == define.ErrNoSuchCtr {
+ return nil, []error{errors.Wrapf(err, "no containers to inspect")}, nil
+ }
+ return nil, nil, err
+ }
+
+ inspect, err := ctr.Inspect(options.Size)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ return []*entities.ContainerInspectReport{
+ {
+ InspectContainerData: inspect,
+ },
+ }, nil, nil
}
- reports := make([]*entities.ContainerInspectReport, 0, len(ctrs))
- for _, c := range ctrs {
- data, err := c.Inspect(options.Size)
+ var (
+ reports = make([]*entities.ContainerInspectReport, 0, len(namesOrIds))
+ errs = []error{}
+ )
+ for _, name := range namesOrIds {
+ ctr, err := ic.Libpod.LookupContainer(name)
if err != nil {
- return nil, err
+ // ErrNoSuchCtr is non-fatal, other errors will be
+ // treated as fatal.
+ if errors.Cause(err) == define.ErrNoSuchCtr {
+ errs = append(errs, errors.Errorf("no such container %s", name))
+ continue
+ }
+ return nil, nil, err
+ }
+
+ inspect, err := ctr.Inspect(options.Size)
+ if err != nil {
+ return nil, nil, err
}
- reports = append(reports, &entities.ContainerInspectReport{InspectContainerData: data})
+
+ reports = append(reports, &entities.ContainerInspectReport{InspectContainerData: inspect})
}
- return reports, nil
+ return reports, errs, nil
}
func (ic *ContainerEngine) ContainerTop(ctx context.Context, options entities.TopOptions) (*entities.StringSliceReport, error) {
@@ -511,9 +542,14 @@ func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []st
}
func (ic *ContainerEngine) ContainerCreate(ctx context.Context, s *specgen.SpecGenerator) (*entities.ContainerCreateReport, error) {
- if err := generate.CompleteSpec(ctx, ic.Libpod, s); err != nil {
+ warn, err := generate.CompleteSpec(ctx, ic.Libpod, s)
+ if err != nil {
return nil, err
}
+ // Print warnings
+ for _, w := range warn {
+ fmt.Fprintf(os.Stderr, "%s\n", w)
+ }
ctr, err := generate.MakeContainer(ctx, ic.Libpod, s)
if err != nil {
return nil, err
@@ -773,9 +809,14 @@ func (ic *ContainerEngine) ContainerDiff(ctx context.Context, nameOrID string, o
}
func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.ContainerRunOptions) (*entities.ContainerRunReport, error) {
- if err := generate.CompleteSpec(ctx, ic.Libpod, opts.Spec); err != nil {
+ warn, err := generate.CompleteSpec(ctx, ic.Libpod, opts.Spec)
+ if err != nil {
return nil, err
}
+ // Print warnings
+ for _, w := range warn {
+ fmt.Fprintf(os.Stderr, "%s\n", w)
+ }
ctr, err := generate.MakeContainer(ctx, ic.Libpod, opts.Spec)
if err != nil {
return nil, err
diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go
index e630d9bc8..0f9ddfec4 100644
--- a/pkg/domain/infra/abi/images.go
+++ b/pkg/domain/infra/abi/images.go
@@ -184,24 +184,28 @@ func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, options entiti
return &entities.ImagePullReport{Images: foundIDs}, nil
}
-func (ir *ImageEngine) Inspect(ctx context.Context, namesOrIDs []string, opts entities.InspectOptions) ([]*entities.ImageInspectReport, error) {
+func (ir *ImageEngine) Inspect(ctx context.Context, namesOrIDs []string, opts entities.InspectOptions) ([]*entities.ImageInspectReport, []error, error) {
reports := []*entities.ImageInspectReport{}
+ errs := []error{}
for _, i := range namesOrIDs {
img, err := ir.Libpod.ImageRuntime().NewFromLocal(i)
if err != nil {
- return nil, err
+ // This is probably a no such image, treat as nonfatal.
+ errs = append(errs, err)
+ continue
}
result, err := img.Inspect(ctx)
if err != nil {
- return nil, err
+ // This is more likely to be fatal.
+ return nil, nil, err
}
report := entities.ImageInspectReport{}
if err := domainUtils.DeepCopy(&report, result); err != nil {
- return nil, err
+ return nil, nil, err
}
reports = append(reports, &report)
}
- return reports, nil
+ return reports, errs, nil
}
func (ir *ImageEngine) Push(ctx context.Context, source string, destination string, options entities.ImagePushOptions) error {
diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go
index 8f6f5c8b7..45fbc64f8 100644
--- a/pkg/domain/infra/tunnel/containers.go
+++ b/pkg/domain/infra/tunnel/containers.go
@@ -185,20 +185,27 @@ func (ic *ContainerEngine) ContainerPrune(ctx context.Context, options entities.
return containers.Prune(ic.ClientCxt, options.Filters)
}
-func (ic *ContainerEngine) ContainerInspect(ctx context.Context, namesOrIds []string, options entities.InspectOptions) ([]*entities.ContainerInspectReport, error) {
- ctrs, err := getContainersByContext(ic.ClientCxt, false, namesOrIds)
- if err != nil {
- return nil, err
- }
- reports := make([]*entities.ContainerInspectReport, 0, len(ctrs))
- for _, con := range ctrs {
- data, err := containers.Inspect(ic.ClientCxt, con.ID, &options.Size)
+func (ic *ContainerEngine) ContainerInspect(ctx context.Context, namesOrIds []string, options entities.InspectOptions) ([]*entities.ContainerInspectReport, []error, error) {
+ var (
+ reports = make([]*entities.ContainerInspectReport, 0, len(namesOrIds))
+ errs = []error{}
+ )
+ for _, name := range namesOrIds {
+ inspect, err := containers.Inspect(ic.ClientCxt, name, &options.Size)
if err != nil {
- return nil, err
+ errModel, ok := err.(entities.ErrorModel)
+ if !ok {
+ return nil, nil, err
+ }
+ if errModel.ResponseCode == 404 {
+ errs = append(errs, errors.Errorf("no such container %q", name))
+ continue
+ }
+ return nil, nil, err
}
- reports = append(reports, &entities.ContainerInspectReport{InspectContainerData: data})
+ reports = append(reports, &entities.ContainerInspectReport{InspectContainerData: inspect})
}
- return reports, nil
+ return reports, errs, nil
}
func (ic *ContainerEngine) ContainerTop(ctx context.Context, options entities.TopOptions) (*entities.StringSliceReport, error) {
@@ -340,6 +347,9 @@ func (ic *ContainerEngine) ContainerCreate(ctx context.Context, s *specgen.SpecG
if err != nil {
return nil, err
}
+ for _, w := range response.Warnings {
+ fmt.Fprintf(os.Stderr, "%s\n", w)
+ }
return &entities.ContainerCreateReport{Id: response.ID}, nil
}
@@ -497,6 +507,9 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta
if err != nil {
return nil, err
}
+ for _, w := range con.Warnings {
+ fmt.Fprintf(os.Stderr, "%s\n", w)
+ }
report := entities.ContainerRunReport{Id: con.ID}
// Attach
if !opts.Detach {
diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go
index 76641696c..35189cb0a 100644
--- a/pkg/domain/infra/tunnel/images.go
+++ b/pkg/domain/infra/tunnel/images.go
@@ -161,16 +161,25 @@ func (ir *ImageEngine) Untag(ctx context.Context, nameOrID string, tags []string
return nil
}
-func (ir *ImageEngine) Inspect(ctx context.Context, namesOrIDs []string, opts entities.InspectOptions) ([]*entities.ImageInspectReport, error) {
+func (ir *ImageEngine) Inspect(ctx context.Context, namesOrIDs []string, opts entities.InspectOptions) ([]*entities.ImageInspectReport, []error, error) {
reports := []*entities.ImageInspectReport{}
+ errs := []error{}
for _, i := range namesOrIDs {
r, err := images.GetImage(ir.ClientCxt, i, &opts.Size)
if err != nil {
- return nil, err
+ errModel, ok := err.(entities.ErrorModel)
+ if !ok {
+ return nil, nil, err
+ }
+ if errModel.ResponseCode == 404 {
+ errs = append(errs, errors.Wrapf(err, "unable to inspect %q", i))
+ continue
+ }
+ return nil, nil, err
}
reports = append(reports, r)
}
- return reports, nil
+ return reports, errs, nil
}
func (ir *ImageEngine) Load(ctx context.Context, opts entities.ImageLoadOptions) (*entities.ImageLoadReport, error) {
diff --git a/pkg/hooks/0.1.0/hook.go b/pkg/hooks/0.1.0/hook.go
index 88a387647..185cc90d1 100644
--- a/pkg/hooks/0.1.0/hook.go
+++ b/pkg/hooks/0.1.0/hook.go
@@ -6,7 +6,6 @@ import (
"errors"
"strings"
- "github.com/containers/libpod/pkg/hooks"
current "github.com/containers/libpod/pkg/hooks/1.0.0"
rspec "github.com/opencontainers/runtime-spec/specs-go"
)
@@ -32,8 +31,9 @@ type Hook struct {
HasBindMounts *bool `json:"hasbindmounts,omitempty"`
}
-func read(content []byte) (hook *current.Hook, err error) {
+func Read(content []byte) (hook *current.Hook, err error) {
var raw Hook
+
if err = json.Unmarshal(content, &raw); err != nil {
return nil, err
}
@@ -86,8 +86,3 @@ func read(content []byte) (hook *current.Hook, err error) {
return hook, nil
}
-
-func init() {
- hooks.Readers[""] = read
- hooks.Readers[Version] = read
-}
diff --git a/pkg/hooks/0.1.0/hook_test.go b/pkg/hooks/0.1.0/hook_test.go
index 66774075e..11881ca0b 100644
--- a/pkg/hooks/0.1.0/hook_test.go
+++ b/pkg/hooks/0.1.0/hook_test.go
@@ -9,7 +9,7 @@ import (
)
func TestGood(t *testing.T) {
- hook, err := read([]byte("{\"hook\": \"/a/b/c\", \"stages\": [\"prestart\"], \"cmds\": [\"sh\"]}"))
+ hook, err := Read([]byte("{\"hook\": \"/a/b/c\", \"stages\": [\"prestart\"], \"cmds\": [\"sh\"]}"))
if err != nil {
t.Fatal(err)
}
@@ -27,7 +27,7 @@ func TestGood(t *testing.T) {
}
func TestInvalidJSON(t *testing.T) {
- _, err := read([]byte("{"))
+ _, err := Read([]byte("{"))
if err == nil {
t.Fatal("unexpected success")
}
@@ -35,7 +35,7 @@ func TestInvalidJSON(t *testing.T) {
}
func TestArguments(t *testing.T) {
- hook, err := read([]byte("{\"hook\": \"/a/b/c\", \"arguments\": [\"d\", \"e\"], \"stages\": [\"prestart\"], \"cmds\": [\"sh\"]}"))
+ hook, err := Read([]byte("{\"hook\": \"/a/b/c\", \"arguments\": [\"d\", \"e\"], \"stages\": [\"prestart\"], \"cmds\": [\"sh\"]}"))
if err != nil {
t.Fatal(err)
}
@@ -54,7 +54,7 @@ func TestArguments(t *testing.T) {
}
func TestEmptyObject(t *testing.T) {
- _, err := read([]byte("{}"))
+ _, err := Read([]byte("{}"))
if err == nil {
t.Fatal("unexpected success")
}
@@ -62,7 +62,7 @@ func TestEmptyObject(t *testing.T) {
}
func TestNoStages(t *testing.T) {
- _, err := read([]byte("{\"hook\": \"/a/b/c\"}"))
+ _, err := Read([]byte("{\"hook\": \"/a/b/c\"}"))
if err == nil {
t.Fatal("unexpected success")
}
@@ -70,7 +70,7 @@ func TestNoStages(t *testing.T) {
}
func TestStage(t *testing.T) {
- hook, err := read([]byte("{\"hook\": \"/a/b/c\", \"stage\": [\"prestart\"]}"))
+ hook, err := Read([]byte("{\"hook\": \"/a/b/c\", \"stage\": [\"prestart\"]}"))
if err != nil {
t.Fatal(err)
}
@@ -85,7 +85,7 @@ func TestStage(t *testing.T) {
}
func TestStagesAndStage(t *testing.T) {
- _, err := read([]byte("{\"hook\": \"/a/b/c\", \"stages\": [\"prestart\"], \"stage\": [\"prestart\"]}"))
+ _, err := Read([]byte("{\"hook\": \"/a/b/c\", \"stages\": [\"prestart\"], \"stage\": [\"prestart\"]}"))
if err == nil {
t.Fatal("unexpected success")
}
@@ -93,7 +93,7 @@ func TestStagesAndStage(t *testing.T) {
}
func TestCmd(t *testing.T) {
- hook, err := read([]byte("{\"hook\": \"/a/b/c\", \"stage\": [\"prestart\"], \"cmd\": [\"sh\"]}"))
+ hook, err := Read([]byte("{\"hook\": \"/a/b/c\", \"stage\": [\"prestart\"], \"cmd\": [\"sh\"]}"))
if err != nil {
t.Fatal(err)
}
@@ -111,7 +111,7 @@ func TestCmd(t *testing.T) {
}
func TestCmdsAndCmd(t *testing.T) {
- _, err := read([]byte("{\"hook\": \"/a/b/c\", \"stages\": [\"prestart\"], \"cmds\": [\"sh\"], \"cmd\": [\"true\"]}"))
+ _, err := Read([]byte("{\"hook\": \"/a/b/c\", \"stages\": [\"prestart\"], \"cmds\": [\"sh\"], \"cmd\": [\"true\"]}"))
if err == nil {
t.Fatal("unexpected success")
}
@@ -119,7 +119,7 @@ func TestCmdsAndCmd(t *testing.T) {
}
func TestAnnotations(t *testing.T) {
- hook, err := read([]byte("{\"hook\": \"/a/b/c\", \"stage\": [\"prestart\"], \"annotations\": [\"a\", \"b\"]}"))
+ hook, err := Read([]byte("{\"hook\": \"/a/b/c\", \"stage\": [\"prestart\"], \"annotations\": [\"a\", \"b\"]}"))
if err != nil {
t.Fatal(err)
}
@@ -137,7 +137,7 @@ func TestAnnotations(t *testing.T) {
}
func TestAnnotation(t *testing.T) {
- hook, err := read([]byte("{\"hook\": \"/a/b/c\", \"stage\": [\"prestart\"], \"annotation\": [\"a\", \"b\"]}"))
+ hook, err := Read([]byte("{\"hook\": \"/a/b/c\", \"stage\": [\"prestart\"], \"annotation\": [\"a\", \"b\"]}"))
if err != nil {
t.Fatal(err)
}
@@ -155,7 +155,7 @@ func TestAnnotation(t *testing.T) {
}
func TestAnnotationsAndAnnotation(t *testing.T) {
- _, err := read([]byte("{\"hook\": \"/a/b/c\", \"stages\": [\"prestart\"], \"annotations\": [\"a\"], \"annotation\": [\"b\"]}"))
+ _, err := Read([]byte("{\"hook\": \"/a/b/c\", \"stages\": [\"prestart\"], \"annotations\": [\"a\"], \"annotation\": [\"b\"]}"))
if err == nil {
t.Fatal("unexpected success")
}
@@ -163,7 +163,7 @@ func TestAnnotationsAndAnnotation(t *testing.T) {
}
func TestHasBindMounts(t *testing.T) {
- hook, err := read([]byte("{\"hook\": \"/a/b/c\", \"stage\": [\"prestart\"], \"hasbindmounts\": true}"))
+ hook, err := Read([]byte("{\"hook\": \"/a/b/c\", \"stage\": [\"prestart\"], \"hasbindmounts\": true}"))
if err != nil {
t.Fatal(err)
}
diff --git a/pkg/hooks/read.go b/pkg/hooks/read.go
index 560ff1899..e20ae9bee 100644
--- a/pkg/hooks/read.go
+++ b/pkg/hooks/read.go
@@ -3,12 +3,12 @@ package hooks
import (
"encoding/json"
- "fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
+ old "github.com/containers/libpod/pkg/hooks/0.1.0"
current "github.com/containers/libpod/pkg/hooks/1.0.0"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -49,7 +49,7 @@ func read(content []byte) (hook *current.Hook, err error) {
}
reader, ok := Readers[ver.Version]
if !ok {
- return nil, fmt.Errorf("unrecognized hook version: %q", ver.Version)
+ return nil, errors.Errorf("unrecognized hook version: %q", ver.Version)
}
hook, err = reader(content)
@@ -95,4 +95,6 @@ func ReadDir(path string, extensionStages []string, hooks map[string]*current.Ho
func init() {
Readers[current.Version] = current.Read
+ Readers[old.Version] = old.Read
+ Readers[""] = old.Read
}
diff --git a/pkg/specgen/generate/container.go b/pkg/specgen/generate/container.go
index 3d70571d5..df27f225b 100644
--- a/pkg/specgen/generate/container.go
+++ b/pkg/specgen/generate/container.go
@@ -5,6 +5,7 @@ import (
"github.com/containers/image/v5/manifest"
"github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/image"
ann "github.com/containers/libpod/pkg/annotations"
envLib "github.com/containers/libpod/pkg/env"
"github.com/containers/libpod/pkg/signal"
@@ -13,91 +14,103 @@ import (
"golang.org/x/sys/unix"
)
-func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerator) error {
- // If a rootfs is used, then there is no image data
- if s.ContainerStorageConfig.Rootfs != "" {
- return nil
- }
-
- newImage, err := r.ImageRuntime().NewFromLocal(s.Image)
- if err != nil {
- return err
- }
-
- _, mediaType, err := newImage.Manifest(ctx)
- if err != nil {
- return err
- }
-
- if s.HealthConfig == nil && mediaType == manifest.DockerV2Schema2MediaType {
- s.HealthConfig, err = newImage.GetHealthCheck(ctx)
+// Fill any missing parts of the spec generator (e.g. from the image).
+// Returns a set of warnings or any fatal error that occurred.
+func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerator) ([]string, error) {
+ var (
+ newImage *image.Image
+ err error
+ )
+
+ // Only add image configuration if we have an image
+ if s.Image != "" {
+ newImage, err = r.ImageRuntime().NewFromLocal(s.Image)
if err != nil {
- return err
+ return nil, err
}
- }
- // Image stop signal
- if s.StopSignal == nil {
- stopSignal, err := newImage.StopSignal(ctx)
+ _, mediaType, err := newImage.Manifest(ctx)
if err != nil {
- return err
+ return nil, err
}
- if stopSignal != "" {
- sig, err := signal.ParseSignalNameOrNumber(stopSignal)
+
+ if s.HealthConfig == nil && mediaType == manifest.DockerV2Schema2MediaType {
+ s.HealthConfig, err = newImage.GetHealthCheck(ctx)
if err != nil {
- return err
+ return nil, err
+ }
+ }
+
+ // Image stop signal
+ if s.StopSignal == nil {
+ stopSignal, err := newImage.StopSignal(ctx)
+ if err != nil {
+ return nil, err
+ }
+ if stopSignal != "" {
+ sig, err := signal.ParseSignalNameOrNumber(stopSignal)
+ if err != nil {
+ return nil, err
+ }
+ s.StopSignal = &sig
}
- s.StopSignal = &sig
}
}
rtc, err := r.GetConfig()
if err != nil {
- return err
+ return nil, err
}
// Get Default Environment
defaultEnvs, err := envLib.ParseSlice(rtc.Containers.Env)
if err != nil {
- return errors.Wrap(err, "Env fields in containers.conf failed to parse")
+ return nil, errors.Wrap(err, "Env fields in containers.conf failed to parse")
}
- // Image envs from the image if they don't exist
- // already, overriding the default environments
- imageEnvs, err := newImage.Env(ctx)
- if err != nil {
- return err
- }
+ var envs map[string]string
- envs, err := envLib.ParseSlice(imageEnvs)
- if err != nil {
- return errors.Wrap(err, "Env fields from image failed to parse")
- }
- s.Env = envLib.Join(envLib.Join(defaultEnvs, envs), s.Env)
+ if newImage != nil {
+ // Image envs from the image if they don't exist
+ // already, overriding the default environments
+ imageEnvs, err := newImage.Env(ctx)
+ if err != nil {
+ return nil, err
+ }
- labels, err := newImage.Labels(ctx)
- if err != nil {
- return err
+ envs, err = envLib.ParseSlice(imageEnvs)
+ if err != nil {
+ return nil, errors.Wrap(err, "Env fields from image failed to parse")
+ }
}
- // labels from the image that dont exist already
- if len(labels) > 0 && s.Labels == nil {
- s.Labels = make(map[string]string)
- }
- for k, v := range labels {
- if _, exists := s.Labels[k]; !exists {
- s.Labels[k] = v
+ s.Env = envLib.Join(envLib.Join(defaultEnvs, envs), s.Env)
+
+ // Labels and Annotations
+ annotations := make(map[string]string)
+ if newImage != nil {
+ labels, err := newImage.Labels(ctx)
+ if err != nil {
+ return nil, err
}
- }
- // annotations
+ // labels from the image that dont exist already
+ if len(labels) > 0 && s.Labels == nil {
+ s.Labels = make(map[string]string)
+ }
+ for k, v := range labels {
+ if _, exists := s.Labels[k]; !exists {
+ s.Labels[k] = v
+ }
+ }
- // Add annotations from the image
- annotations, err := newImage.Annotations(ctx)
- if err != nil {
- return err
- }
- for k, v := range annotations {
- annotations[k] = v
+ // Add annotations from the image
+ imgAnnotations, err := newImage.Annotations(ctx)
+ if err != nil {
+ return nil, err
+ }
+ for k, v := range imgAnnotations {
+ annotations[k] = v
+ }
}
// in the event this container is in a pod, and the pod has an infra container
@@ -121,40 +134,42 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat
s.Annotations = annotations
// workdir
- workingDir, err := newImage.WorkingDir(ctx)
- if err != nil {
- return err
- }
- if len(s.WorkDir) < 1 && len(workingDir) > 1 {
- s.WorkDir = workingDir
+ if newImage != nil {
+ workingDir, err := newImage.WorkingDir(ctx)
+ if err != nil {
+ return nil, err
+ }
+ if len(s.WorkDir) < 1 && len(workingDir) > 1 {
+ s.WorkDir = workingDir
+ }
}
if len(s.SeccompProfilePath) < 1 {
p, err := libpod.DefaultSeccompPath()
if err != nil {
- return err
+ return nil, err
}
s.SeccompProfilePath = p
}
- if len(s.User) == 0 {
+ if len(s.User) == 0 && newImage != nil {
s.User, err = newImage.User(ctx)
if err != nil {
- return err
+ return nil, err
}
}
if err := finishThrottleDevices(s); err != nil {
- return err
+ return nil, err
}
// Unless already set via the CLI, check if we need to disable process
// labels or set the defaults.
if len(s.SelinuxOpts) == 0 {
if err := setLabelOpts(s, r, s.PidNS, s.IpcNS); err != nil {
- return err
+ return nil, err
}
}
- return nil
+ return verifyContainerResources(s)
}
// finishThrottleDevices takes the temporary representation of the throttle
diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go
index 33075b543..2f7100e7e 100644
--- a/pkg/specgen/generate/container_create.go
+++ b/pkg/specgen/generate/container_create.go
@@ -7,7 +7,6 @@ import (
"github.com/containers/common/pkg/config"
"github.com/containers/libpod/libpod"
- "github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/pkg/specgen"
"github.com/containers/libpod/pkg/util"
@@ -16,7 +15,9 @@ import (
"github.com/sirupsen/logrus"
)
-// MakeContainer creates a container based on the SpecGenerator
+// MakeContainer creates a container based on the SpecGenerator.
+// Returns the created, container and any warnings resulting from creating the
+// container, or an error.
func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGenerator) (*libpod.Container, error) {
rtc, err := rt.GetConfig()
if err != nil {
@@ -249,9 +250,6 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.
// Default used if not overridden on command line
if s.RestartPolicy != "" {
- if s.RestartPolicy == "unless-stopped" {
- return nil, errors.Wrapf(define.ErrInvalidArg, "the unless-stopped restart policy is not supported")
- }
if s.RestartRetries != nil {
options = append(options, libpod.WithRestartRetries(*s.RestartRetries))
}
diff --git a/pkg/specgen/generate/validate.go b/pkg/specgen/generate/validate.go
new file mode 100644
index 000000000..bb3ca9907
--- /dev/null
+++ b/pkg/specgen/generate/validate.go
@@ -0,0 +1,159 @@
+package generate
+
+import (
+ "github.com/containers/common/pkg/sysinfo"
+ "github.com/containers/libpod/pkg/cgroups"
+ "github.com/containers/libpod/pkg/specgen"
+ "github.com/pkg/errors"
+)
+
+// Verify resource limits are sanely set, removing any limits that are not
+// possible with the current cgroups config.
+func verifyContainerResources(s *specgen.SpecGenerator) ([]string, error) {
+ warnings := []string{}
+
+ cgroup2, err := cgroups.IsCgroup2UnifiedMode()
+ if err != nil || cgroup2 {
+ return warnings, err
+ }
+
+ sysInfo := sysinfo.New(true)
+
+ if s.ResourceLimits == nil {
+ return warnings, nil
+ }
+
+ // Memory checks
+ if s.ResourceLimits.Memory != nil {
+ memory := s.ResourceLimits.Memory
+ if memory.Limit != nil && !sysInfo.MemoryLimit {
+ warnings = append(warnings, "Your kernel does not support memory limit capabilities or the cgroup is not mounted. Limitation discarded.")
+ memory.Limit = nil
+ memory.Swap = nil
+ }
+ if memory.Limit != nil && memory.Swap != nil && !sysInfo.SwapLimit {
+ warnings = append(warnings, "Your kernel does not support swap limit capabilities,or the cgroup is not mounted. Memory limited without swap.")
+ memory.Swap = nil
+ }
+ if memory.Limit != nil && memory.Swap != nil && *memory.Swap < *memory.Limit {
+ return warnings, errors.New("minimum memoryswap limit should be larger than memory limit, see usage")
+ }
+ if memory.Limit == nil && memory.Swap != nil {
+ return warnings, errors.New("you should always set a memory limit when using a memoryswap limit, see usage")
+ }
+ if memory.Swappiness != nil {
+ if !sysInfo.MemorySwappiness {
+ warnings = append(warnings, "Your kernel does not support memory swappiness capabilities, or the cgroup is not mounted. Memory swappiness discarded.")
+ memory.Swappiness = nil
+ } else {
+ if *memory.Swappiness < 0 || *memory.Swappiness > 100 {
+ return warnings, errors.Errorf("invalid value: %v, valid memory swappiness range is 0-100", *memory.Swappiness)
+ }
+ }
+ }
+ if memory.Reservation != nil && !sysInfo.MemoryReservation {
+ warnings = append(warnings, "Your kernel does not support memory soft limit capabilities or the cgroup is not mounted. Limitation discarded.")
+ memory.Reservation = nil
+ }
+ if memory.Limit != nil && memory.Reservation != nil && *memory.Limit < *memory.Reservation {
+ return warnings, errors.New("minimum memory limit cannot be less than memory reservation limit, see usage")
+ }
+ if memory.Kernel != nil && !sysInfo.KernelMemory {
+ warnings = append(warnings, "Your kernel does not support kernel memory limit capabilities or the cgroup is not mounted. Limitation discarded.")
+ memory.Kernel = nil
+ }
+ if memory.DisableOOMKiller != nil && *memory.DisableOOMKiller && !sysInfo.OomKillDisable {
+ warnings = append(warnings, "Your kernel does not support OomKillDisable. OomKillDisable discarded.")
+ memory.DisableOOMKiller = nil
+ }
+ }
+
+ // Pids checks
+ if s.ResourceLimits.Pids != nil {
+ pids := s.ResourceLimits.Pids
+ // TODO: Should this be 0, or checking that ResourceLimits.Pids
+ // is set at all?
+ if pids.Limit > 0 && !sysInfo.PidsLimit {
+ warnings = append(warnings, "Your kernel does not support pids limit capabilities or the cgroup is not mounted. PIDs limit discarded.")
+ s.ResourceLimits.Pids = nil
+ }
+ }
+
+ // CPU Checks
+ if s.ResourceLimits.CPU != nil {
+ cpu := s.ResourceLimits.CPU
+ if cpu.Shares != nil && !sysInfo.CPUShares {
+ warnings = append(warnings, "Your kernel does not support CPU shares or the cgroup is not mounted. Shares discarded.")
+ cpu.Shares = nil
+ }
+ if cpu.Period != nil && !sysInfo.CPUCfsPeriod {
+ warnings = append(warnings, "Your kernel does not support CPU cfs period or the cgroup is not mounted. Period discarded.")
+ cpu.Period = nil
+ }
+ if cpu.Period != nil && (*cpu.Period < 1000 || *cpu.Period > 1000000) {
+ return warnings, errors.New("CPU cfs period cannot be less than 1ms (i.e. 1000) or larger than 1s (i.e. 1000000)")
+ }
+ if cpu.Quota != nil && !sysInfo.CPUCfsQuota {
+ warnings = append(warnings, "Your kernel does not support CPU cfs quota or the cgroup is not mounted. Quota discarded.")
+ cpu.Quota = nil
+ }
+ if cpu.Quota != nil && *cpu.Quota < 1000 {
+ return warnings, errors.New("CPU cfs quota cannot be less than 1ms (i.e. 1000)")
+ }
+ if (cpu.Cpus != "" || cpu.Mems != "") && !sysInfo.Cpuset {
+ warnings = append(warnings, "Your kernel does not support cpuset or the cgroup is not mounted. CPUset discarded.")
+ cpu.Cpus = ""
+ cpu.Mems = ""
+ }
+
+ cpusAvailable, err := sysInfo.IsCpusetCpusAvailable(cpu.Cpus)
+ if err != nil {
+ return warnings, errors.Errorf("invalid value %s for cpuset cpus", cpu.Cpus)
+ }
+ if !cpusAvailable {
+ return warnings, errors.Errorf("requested CPUs are not available - requested %s, available: %s", cpu.Cpus, sysInfo.Cpus)
+ }
+
+ memsAvailable, err := sysInfo.IsCpusetMemsAvailable(cpu.Mems)
+ if err != nil {
+ return warnings, errors.Errorf("invalid value %s for cpuset mems", cpu.Mems)
+ }
+ if !memsAvailable {
+ return warnings, errors.Errorf("requested memory nodes are not available - requested %s, available: %s", cpu.Mems, sysInfo.Mems)
+ }
+ }
+
+ // Blkio checks
+ if s.ResourceLimits.BlockIO != nil {
+ blkio := s.ResourceLimits.BlockIO
+ if blkio.Weight != nil && !sysInfo.BlkioWeight {
+ warnings = append(warnings, "Your kernel does not support Block I/O weight or the cgroup is not mounted. Weight discarded.")
+ blkio.Weight = nil
+ }
+ if blkio.Weight != nil && (*blkio.Weight > 1000 || *blkio.Weight < 10) {
+ return warnings, errors.New("range of blkio weight is from 10 to 1000")
+ }
+ if len(blkio.WeightDevice) > 0 && !sysInfo.BlkioWeightDevice {
+ warnings = append(warnings, "Your kernel does not support Block I/O weight_device or the cgroup is not mounted. Weight-device discarded.")
+ blkio.WeightDevice = nil
+ }
+ if len(blkio.ThrottleReadBpsDevice) > 0 && !sysInfo.BlkioReadBpsDevice {
+ warnings = append(warnings, "Your kernel does not support BPS Block I/O read limit or the cgroup is not mounted. Block I/O BPS read limit discarded")
+ blkio.ThrottleReadBpsDevice = nil
+ }
+ if len(blkio.ThrottleWriteBpsDevice) > 0 && !sysInfo.BlkioWriteBpsDevice {
+ warnings = append(warnings, "Your kernel does not support BPS Block I/O write limit or the cgroup is not mounted. Block I/O BPS write limit discarded.")
+ blkio.ThrottleWriteBpsDevice = nil
+ }
+ if len(blkio.ThrottleReadIOPSDevice) > 0 && !sysInfo.BlkioReadIOpsDevice {
+ warnings = append(warnings, "Your kernel does not support IOPS Block read limit or the cgroup is not mounted. Block I/O IOPS read limit discarded.")
+ blkio.ThrottleReadIOPSDevice = nil
+ }
+ if len(blkio.ThrottleWriteIOPSDevice) > 0 && !sysInfo.BlkioWriteIOpsDevice {
+ warnings = append(warnings, "Your kernel does not support IOPS Block I/O write limit or the cgroup is not mounted. Block I/O IOPS write limit discarded.")
+ blkio.ThrottleWriteIOPSDevice = nil
+ }
+ }
+
+ return warnings, nil
+}
diff --git a/pkg/systemd/generate/containers.go b/pkg/systemd/generate/containers.go
index 4180022cb..16ff0b821 100644
--- a/pkg/systemd/generate/containers.go
+++ b/pkg/systemd/generate/containers.go
@@ -83,9 +83,7 @@ ExecStartPre={{.ExecStartPre}}
{{- end}}
ExecStart={{.ExecStart}}
ExecStop={{.ExecStop}}
-{{- if .ExecStopPost}}
ExecStopPost={{.ExecStopPost}}
-{{- end}}
PIDFile={{.PIDFile}}
KillMode=none
Type=forking
@@ -170,6 +168,7 @@ func executeContainerTemplate(info *containerInfo, options entities.GenerateSyst
info.EnvVariable = EnvVariable
info.ExecStart = "{{.Executable}} start {{.ContainerNameOrID}}"
info.ExecStop = "{{.Executable}} stop {{if (ge .StopTimeout 0)}}-t {{.StopTimeout}}{{end}} {{.ContainerNameOrID}}"
+ info.ExecStopPost = "{{.Executable}} stop {{if (ge .StopTimeout 0)}}-t {{.StopTimeout}}{{end}} {{.ContainerNameOrID}}"
// Assemble the ExecStart command when creating a new container.
//
diff --git a/pkg/systemd/generate/containers_test.go b/pkg/systemd/generate/containers_test.go
index 8d3ea1ca0..5f35c31f5 100644
--- a/pkg/systemd/generate/containers_test.go
+++ b/pkg/systemd/generate/containers_test.go
@@ -50,6 +50,7 @@ Environment=PODMAN_SYSTEMD_UNIT=%n
Restart=always
ExecStart=/usr/bin/podman start 639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401
ExecStop=/usr/bin/podman stop -t 10 639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401
+ExecStopPost=/usr/bin/podman stop -t 10 639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401
PIDFile=/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid
KillMode=none
Type=forking
@@ -71,6 +72,7 @@ Environment=PODMAN_SYSTEMD_UNIT=%n
Restart=always
ExecStart=/usr/bin/podman start foobar
ExecStop=/usr/bin/podman stop -t 10 foobar
+ExecStopPost=/usr/bin/podman stop -t 10 foobar
PIDFile=/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid
KillMode=none
Type=forking
@@ -96,6 +98,7 @@ Environment=PODMAN_SYSTEMD_UNIT=%n
Restart=always
ExecStart=/usr/bin/podman start foobar
ExecStop=/usr/bin/podman stop -t 10 foobar
+ExecStopPost=/usr/bin/podman stop -t 10 foobar
PIDFile=/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid
KillMode=none
Type=forking
diff --git a/pkg/systemd/generate/pods.go b/pkg/systemd/generate/pods.go
index 367b8381f..1bd0c7bce 100644
--- a/pkg/systemd/generate/pods.go
+++ b/pkg/systemd/generate/pods.go
@@ -82,9 +82,7 @@ ExecStartPre={{.ExecStartPre2}}
{{- end}}
ExecStart={{.ExecStart}}
ExecStop={{.ExecStop}}
-{{- if .ExecStopPost}}
ExecStopPost={{.ExecStopPost}}
-{{- end}}
PIDFile={{.PIDFile}}
KillMode=none
Type=forking
@@ -236,6 +234,7 @@ func executePodTemplate(info *podInfo, options entities.GenerateSystemdOptions)
info.EnvVariable = EnvVariable
info.ExecStart = "{{.Executable}} start {{.InfraNameOrID}}"
info.ExecStop = "{{.Executable}} stop {{if (ge .StopTimeout 0)}}-t {{.StopTimeout}}{{end}} {{.InfraNameOrID}}"
+ info.ExecStopPost = "{{.Executable}} stop {{if (ge .StopTimeout 0)}}-t {{.StopTimeout}}{{end}} {{.InfraNameOrID}}"
// Assemble the ExecStart command when creating a new pod.
//
diff --git a/pkg/systemd/generate/pods_test.go b/pkg/systemd/generate/pods_test.go
index f7ce33a30..e12222317 100644
--- a/pkg/systemd/generate/pods_test.go
+++ b/pkg/systemd/generate/pods_test.go
@@ -52,6 +52,7 @@ Environment=PODMAN_SYSTEMD_UNIT=%n
Restart=always
ExecStart=/usr/bin/podman start jadda-jadda-infra
ExecStop=/usr/bin/podman stop -t 10 jadda-jadda-infra
+ExecStopPost=/usr/bin/podman stop -t 10 jadda-jadda-infra
PIDFile=/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid
KillMode=none
Type=forking
diff --git a/test/e2e/create_test.go b/test/e2e/create_test.go
index 822e470f2..52ce0b46a 100644
--- a/test/e2e/create_test.go
+++ b/test/e2e/create_test.go
@@ -401,6 +401,20 @@ var _ = Describe("Podman create", func() {
Expect(session.ExitCode()).To(Not(Equal(0)))
})
+ It("podman create with --restart-policy unless-stopped", func() {
+ ctrName := "testctr"
+ unlessStopped := "unless-stopped"
+ session := podmanTest.Podman([]string{"create", "-t", "--restart", unlessStopped, "--name", ctrName, ALPINE, "/bin/sh"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ inspect := podmanTest.Podman([]string{"inspect", ctrName})
+ inspect.WaitWithDefaultTimeout()
+ data := inspect.InspectContainerToJSON()
+ Expect(len(data)).To(Equal(1))
+ Expect(data[0].HostConfig.RestartPolicy.Name).To(Equal(unlessStopped))
+ })
+
It("podman create with -m 1000000 sets swap to 2000000", func() {
numMem := 1000000
ctrName := "testCtr"
diff --git a/test/e2e/inspect_test.go b/test/e2e/inspect_test.go
index 62f69f1c1..2fad38a36 100644
--- a/test/e2e/inspect_test.go
+++ b/test/e2e/inspect_test.go
@@ -223,4 +223,44 @@ var _ = Describe("Podman inspect", func() {
Expect(baseJSON[0].ID).To(Equal(ctrJSON[0].ID))
})
+
+ It("podman inspect always produces a valid array", func() {
+ baseInspect := podmanTest.Podman([]string{"inspect", "doesNotExist"})
+ baseInspect.WaitWithDefaultTimeout()
+ Expect(baseInspect.ExitCode()).To(Not(Equal(0)))
+ emptyJSON := baseInspect.InspectContainerToJSON()
+ Expect(len(emptyJSON)).To(Equal(0))
+ })
+
+ It("podman inspect one container with not exist returns 1-length valid array", func() {
+ ctrName := "testCtr"
+ create := podmanTest.Podman([]string{"create", "--name", ctrName, ALPINE, "sh"})
+ create.WaitWithDefaultTimeout()
+ Expect(create.ExitCode()).To(Equal(0))
+
+ baseInspect := podmanTest.Podman([]string{"inspect", ctrName, "doesNotExist"})
+ baseInspect.WaitWithDefaultTimeout()
+ Expect(baseInspect.ExitCode()).To(Not(Equal(0)))
+ baseJSON := baseInspect.InspectContainerToJSON()
+ Expect(len(baseJSON)).To(Equal(1))
+ Expect(baseJSON[0].Name).To(Equal(ctrName))
+ })
+
+ It("podman inspect container + image with same name gives container", func() {
+ ctrName := "testcontainer"
+ create := podmanTest.PodmanNoCache([]string{"create", "--name", ctrName, ALPINE, "sh"})
+ create.WaitWithDefaultTimeout()
+ Expect(create.ExitCode()).To(Equal(0))
+
+ tag := podmanTest.PodmanNoCache([]string{"tag", ALPINE, ctrName + ":latest"})
+ tag.WaitWithDefaultTimeout()
+ Expect(tag.ExitCode()).To(Equal(0))
+
+ baseInspect := podmanTest.Podman([]string{"inspect", ctrName})
+ baseInspect.WaitWithDefaultTimeout()
+ Expect(baseInspect.ExitCode()).To(Equal(0))
+ baseJSON := baseInspect.InspectContainerToJSON()
+ Expect(len(baseJSON)).To(Equal(1))
+ Expect(baseJSON[0].Name).To(Equal(ctrName))
+ })
})
diff --git a/test/e2e/logs_test.go b/test/e2e/logs_test.go
index f9446e0c6..c12ae9c86 100644
--- a/test/e2e/logs_test.go
+++ b/test/e2e/logs_test.go
@@ -283,21 +283,21 @@ var _ = Describe("Podman logs", func() {
results.WaitWithDefaultTimeout()
Expect(results).To(Exit(0))
- // Verify that the cleanup process worked correctly and we can recreate a container with the same name
- logc = podmanTest.Podman([]string{"run", "--rm", "--name", containerName, "-dt", ALPINE, "true"})
- logc.WaitWithDefaultTimeout()
- Expect(logc).To(Exit(0))
- })
-
- It("follow output stopped container", func() {
- containerName := "logs-f"
-
- logc := podmanTest.Podman([]string{"run", "--name", containerName, "-d", ALPINE})
- logc.WaitWithDefaultTimeout()
- Expect(logc).To(Exit(0))
-
- results := podmanTest.Podman([]string{"logs", "-f", containerName})
- results.WaitWithDefaultTimeout()
- Expect(results).To(Exit(0))
+ // TODO: we should actually check for two podman lines,
+ // but as of 2020-06-17 there's a race condition in which
+ // 'logs -f' may not catch all output from a container
+ Expect(results.OutputToString()).To(ContainSubstring("podman"))
+
+ // Container should now be terminatING or terminatED, but we
+ // have no guarantee of which: 'logs -f' does not necessarily
+ // wait for cleanup. Run 'inspect' and accept either state.
+ inspect := podmanTest.Podman([]string{"container", "inspect", "--format", "{{.State.Status}}", containerName})
+ inspect.WaitWithDefaultTimeout()
+ if inspect.ExitCode() == 0 {
+ Expect(inspect.OutputToString()).To(Equal("exited"))
+ // TODO: add 2-second wait loop to confirm cleanup
+ } else {
+ Expect(inspect.ErrorToString()).To(ContainSubstring("no such container"))
+ }
})
})
diff --git a/vendor/github.com/containers/image/v5/copy/copy.go b/vendor/github.com/containers/image/v5/copy/copy.go
index 9fc0e5123..7482fb458 100644
--- a/vendor/github.com/containers/image/v5/copy/copy.go
+++ b/vendor/github.com/containers/image/v5/copy/copy.go
@@ -659,7 +659,7 @@ func (c *copier) copyOneImage(ctx context.Context, policyContext *signature.Poli
// With !ic.canModifyManifest, that would just be a string of repeated failures for the same reason,
// so let’s bail out early and with a better error message.
if !ic.canModifyManifest {
- return nil, "", "", errors.Wrap(err, "Writing manifest failed (and converting it is not possible)")
+ return nil, "", "", errors.Wrap(err, "Writing manifest failed (and converting it is not possible, image is signed or the destination specifies a digest)")
}
// errs is a list of errors when trying various manifest types. Also serves as an "upload succeeded" flag when set to nil.
@@ -757,7 +757,7 @@ func (ic *imageCopier) updateEmbeddedDockerReference() error {
}
if !ic.canModifyManifest {
- return errors.Errorf("Copying a schema1 image with an embedded Docker reference to %s (Docker reference %s) would invalidate existing signatures. Explicitly enable signature removal to proceed anyway",
+ return errors.Errorf("Copying a schema1 image with an embedded Docker reference to %s (Docker reference %s) would change the manifest, which is not possible (image is signed or the destination specifies a digest)",
transports.ImageName(ic.c.dest.Reference()), destRef.String())
}
ic.manifestUpdates.EmbeddedDockerReference = destRef
@@ -784,7 +784,7 @@ func (ic *imageCopier) copyLayers(ctx context.Context) error {
// If we only need to check authorization, no updates required.
if updatedSrcInfos != nil && !reflect.DeepEqual(srcInfos, updatedSrcInfos) {
if !ic.canModifyManifest {
- return errors.Errorf("Internal error: copyLayers() needs to use an updated manifest but that was known to be forbidden")
+ return errors.Errorf("Copying this image requires changing layer representation, which is not possible (image is signed or the destination specifies a digest)")
}
srcInfos = updatedSrcInfos
srcInfosUpdated = true
@@ -1060,6 +1060,14 @@ func (ic *imageCopier) copyLayer(ctx context.Context, srcInfo types.BlobInfo, to
logrus.Debugf("Skipping blob %s (already present):", srcInfo.Digest)
bar := ic.c.createProgressBar(pool, srcInfo, "blob", "skipped: already exists")
bar.SetTotal(0, true)
+
+ // Throw an event that the layer has been skipped
+ if ic.c.progress != nil && ic.c.progressInterval > 0 {
+ ic.c.progress <- types.ProgressProperties{
+ Event: types.ProgressEventSkipped,
+ Artifact: srcInfo,
+ }
+ }
return blobInfo, cachedDiffID, nil
}
}
diff --git a/vendor/github.com/containers/image/v5/oci/layout/oci_dest.go b/vendor/github.com/containers/image/v5/oci/layout/oci_dest.go
index fb0449ca5..48a32315b 100644
--- a/vendor/github.com/containers/image/v5/oci/layout/oci_dest.go
+++ b/vendor/github.com/containers/image/v5/oci/layout/oci_dest.go
@@ -279,7 +279,7 @@ func (d *ociImageDestination) addManifest(desc *imgspecv1.Descriptor) {
// If it has the same digest as another entry in the index, we already overwrote the file,
// so just pick up the other information.
for i, manifest := range d.index.Manifests {
- if manifest.Digest == desc.Digest {
+ if manifest.Digest == desc.Digest && manifest.Annotations[imgspecv1.AnnotationRefName] == "" {
// Replace it completely.
d.index.Manifests[i] = *desc
return
diff --git a/vendor/github.com/containers/image/v5/types/types.go b/vendor/github.com/containers/image/v5/types/types.go
index d469e03b5..4f624cf33 100644
--- a/vendor/github.com/containers/image/v5/types/types.go
+++ b/vendor/github.com/containers/image/v5/types/types.go
@@ -604,6 +604,10 @@ const (
// ProgressEventDone is fired when the data transfer has been finished for
// the specific artifact
ProgressEventDone
+
+ // ProgressEventSkipped is fired when the artifact has been skipped because
+ // its already available at the destination
+ ProgressEventSkipped
)
// ProgressProperties is used to pass information from the copy code to a monitor which
diff --git a/vendor/github.com/containers/image/v5/version/version.go b/vendor/github.com/containers/image/v5/version/version.go
index 7cf412723..114bce387 100644
--- a/vendor/github.com/containers/image/v5/version/version.go
+++ b/vendor/github.com/containers/image/v5/version/version.go
@@ -8,10 +8,10 @@ const (
// VersionMinor is for functionality in a backwards-compatible manner
VersionMinor = 5
// VersionPatch is for backwards-compatible bug fixes
- VersionPatch = 0
+ VersionPatch = 1
// VersionDev indicates development branch. Releases will be empty string.
- VersionDev = "-dev"
+ VersionDev = ""
)
// Version is the specification version that the package types support.
diff --git a/vendor/github.com/klauspost/compress/fse/bitreader.go b/vendor/github.com/klauspost/compress/fse/bitreader.go
index b9db204f5..f65eb3909 100644
--- a/vendor/github.com/klauspost/compress/fse/bitreader.go
+++ b/vendor/github.com/klauspost/compress/fse/bitreader.go
@@ -6,6 +6,7 @@
package fse
import (
+ "encoding/binary"
"errors"
"io"
)
@@ -34,8 +35,12 @@ func (b *bitReader) init(in []byte) error {
}
b.bitsRead = 64
b.value = 0
- b.fill()
- b.fill()
+ if len(in) >= 8 {
+ b.fillFastStart()
+ } else {
+ b.fill()
+ b.fill()
+ }
b.bitsRead += 8 - uint8(highBits(uint32(v)))
return nil
}
@@ -63,8 +68,9 @@ func (b *bitReader) fillFast() {
if b.bitsRead < 32 {
return
}
- // Do single re-slice to avoid bounds checks.
- v := b.in[b.off-4 : b.off]
+ // 2 bounds checks.
+ v := b.in[b.off-4:]
+ v = v[:4]
low := (uint32(v[0])) | (uint32(v[1]) << 8) | (uint32(v[2]) << 16) | (uint32(v[3]) << 24)
b.value = (b.value << 32) | uint64(low)
b.bitsRead -= 32
@@ -77,7 +83,8 @@ func (b *bitReader) fill() {
return
}
if b.off > 4 {
- v := b.in[b.off-4 : b.off]
+ v := b.in[b.off-4:]
+ v = v[:4]
low := (uint32(v[0])) | (uint32(v[1]) << 8) | (uint32(v[2]) << 16) | (uint32(v[3]) << 24)
b.value = (b.value << 32) | uint64(low)
b.bitsRead -= 32
@@ -91,9 +98,17 @@ func (b *bitReader) fill() {
}
}
+// fillFastStart() assumes the bitreader is empty and there is at least 8 bytes to read.
+func (b *bitReader) fillFastStart() {
+ // Do single re-slice to avoid bounds checks.
+ b.value = binary.LittleEndian.Uint64(b.in[b.off-8:])
+ b.bitsRead = 0
+ b.off -= 8
+}
+
// finished returns true if all bits have been read from the bit stream.
func (b *bitReader) finished() bool {
- return b.off == 0 && b.bitsRead >= 64
+ return b.bitsRead >= 64 && b.off == 0
}
// close the bitstream and returns an error if out-of-buffer reads occurred.
diff --git a/vendor/github.com/klauspost/compress/fse/bytereader.go b/vendor/github.com/klauspost/compress/fse/bytereader.go
index f228a46cd..abade2d60 100644
--- a/vendor/github.com/klauspost/compress/fse/bytereader.go
+++ b/vendor/github.com/klauspost/compress/fse/bytereader.go
@@ -25,19 +25,10 @@ func (b *byteReader) advance(n uint) {
b.off += int(n)
}
-// Int32 returns a little endian int32 starting at current offset.
-func (b byteReader) Int32() int32 {
- b2 := b.b[b.off : b.off+4 : b.off+4]
- v3 := int32(b2[3])
- v2 := int32(b2[2])
- v1 := int32(b2[1])
- v0 := int32(b2[0])
- return v0 | (v1 << 8) | (v2 << 16) | (v3 << 24)
-}
-
// Uint32 returns a little endian uint32 starting at current offset.
func (b byteReader) Uint32() uint32 {
- b2 := b.b[b.off : b.off+4 : b.off+4]
+ b2 := b.b[b.off:]
+ b2 = b2[:4]
v3 := uint32(b2[3])
v2 := uint32(b2[2])
v1 := uint32(b2[1])
diff --git a/vendor/github.com/klauspost/compress/huff0/README.md b/vendor/github.com/klauspost/compress/huff0/README.md
index 0a8448ce9..e12da4db2 100644
--- a/vendor/github.com/klauspost/compress/huff0/README.md
+++ b/vendor/github.com/klauspost/compress/huff0/README.md
@@ -12,8 +12,6 @@ but it can be used as a secondary step to compressors (like Snappy) that does no
* [Godoc documentation](https://godoc.org/github.com/klauspost/compress/huff0)
-THIS PACKAGE IS NOT CONSIDERED STABLE AND API OR ENCODING MAY CHANGE IN THE FUTURE.
-
## News
* Mar 2018: First implementation released. Consider this beta software for now.
@@ -75,6 +73,8 @@ which can be given to the decompressor.
Decompressing is done by calling the [`Decompress1X`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch.Decompress1X)
or [`Decompress4X`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch.Decompress4X) function.
+For concurrently decompressing content with a fixed table a stateless [`Decoder`](https://godoc.org/github.com/klauspost/compress/huff0#Decoder) can be requested which will remain correct as long as the scratch is unchanged. The capacity of the provided slice indicates the expected output size.
+
You must provide the output from the compression stage, at exactly the size you got back. If you receive an error back
your input was likely corrupted.
@@ -84,4 +84,4 @@ There are no integrity checks, so relying on errors from the decompressor does n
# Contributing
Contributions are always welcome. Be aware that adding public functions will require good justification and breaking
-changes will likely not be accepted. If in doubt open an issue before writing the PR. \ No newline at end of file
+changes will likely not be accepted. If in doubt open an issue before writing the PR.
diff --git a/vendor/github.com/klauspost/compress/huff0/bitreader.go b/vendor/github.com/klauspost/compress/huff0/bitreader.go
index 7d0903c70..a4979e886 100644
--- a/vendor/github.com/klauspost/compress/huff0/bitreader.go
+++ b/vendor/github.com/klauspost/compress/huff0/bitreader.go
@@ -6,6 +6,7 @@
package huff0
import (
+ "encoding/binary"
"errors"
"io"
)
@@ -34,29 +35,16 @@ func (b *bitReader) init(in []byte) error {
}
b.bitsRead = 64
b.value = 0
- b.fill()
- b.fill()
+ if len(in) >= 8 {
+ b.fillFastStart()
+ } else {
+ b.fill()
+ b.fill()
+ }
b.bitsRead += 8 - uint8(highBit32(uint32(v)))
return nil
}
-// getBits will return n bits. n can be 0.
-func (b *bitReader) getBits(n uint8) uint16 {
- if n == 0 || b.bitsRead >= 64 {
- return 0
- }
- return b.getBitsFast(n)
-}
-
-// getBitsFast requires that at least one bit is requested every time.
-// There are no checks if the buffer is filled.
-func (b *bitReader) getBitsFast(n uint8) uint16 {
- const regMask = 64 - 1
- v := uint16((b.value << (b.bitsRead & regMask)) >> ((regMask + 1 - n) & regMask))
- b.bitsRead += n
- return v
-}
-
// peekBitsFast requires that at least one bit is requested every time.
// There are no checks if the buffer is filled.
func (b *bitReader) peekBitsFast(n uint8) uint16 {
@@ -71,21 +59,36 @@ func (b *bitReader) fillFast() {
if b.bitsRead < 32 {
return
}
- // Do single re-slice to avoid bounds checks.
+
+ // 2 bounds checks.
v := b.in[b.off-4 : b.off]
+ v = v[:4]
low := (uint32(v[0])) | (uint32(v[1]) << 8) | (uint32(v[2]) << 16) | (uint32(v[3]) << 24)
b.value = (b.value << 32) | uint64(low)
b.bitsRead -= 32
b.off -= 4
}
+func (b *bitReader) advance(n uint8) {
+ b.bitsRead += n
+}
+
+// fillFastStart() assumes the bitreader is empty and there is at least 8 bytes to read.
+func (b *bitReader) fillFastStart() {
+ // Do single re-slice to avoid bounds checks.
+ b.value = binary.LittleEndian.Uint64(b.in[b.off-8:])
+ b.bitsRead = 0
+ b.off -= 8
+}
+
// fill() will make sure at least 32 bits are available.
func (b *bitReader) fill() {
if b.bitsRead < 32 {
return
}
if b.off > 4 {
- v := b.in[b.off-4 : b.off]
+ v := b.in[b.off-4:]
+ v = v[:4]
low := (uint32(v[0])) | (uint32(v[1]) << 8) | (uint32(v[2]) << 16) | (uint32(v[3]) << 24)
b.value = (b.value << 32) | uint64(low)
b.bitsRead -= 32
@@ -113,3 +116,214 @@ func (b *bitReader) close() error {
}
return nil
}
+
+// bitReader reads a bitstream in reverse.
+// The last set bit indicates the start of the stream and is used
+// for aligning the input.
+type bitReaderBytes struct {
+ in []byte
+ off uint // next byte to read is at in[off - 1]
+ value uint64
+ bitsRead uint8
+}
+
+// init initializes and resets the bit reader.
+func (b *bitReaderBytes) init(in []byte) error {
+ if len(in) < 1 {
+ return errors.New("corrupt stream: too short")
+ }
+ b.in = in
+ b.off = uint(len(in))
+ // The highest bit of the last byte indicates where to start
+ v := in[len(in)-1]
+ if v == 0 {
+ return errors.New("corrupt stream, did not find end of stream")
+ }
+ b.bitsRead = 64
+ b.value = 0
+ if len(in) >= 8 {
+ b.fillFastStart()
+ } else {
+ b.fill()
+ b.fill()
+ }
+ b.advance(8 - uint8(highBit32(uint32(v))))
+ return nil
+}
+
+// peekBitsFast requires that at least one bit is requested every time.
+// There are no checks if the buffer is filled.
+func (b *bitReaderBytes) peekByteFast() uint8 {
+ got := uint8(b.value >> 56)
+ return got
+}
+
+func (b *bitReaderBytes) advance(n uint8) {
+ b.bitsRead += n
+ b.value <<= n & 63
+}
+
+// fillFast() will make sure at least 32 bits are available.
+// There must be at least 4 bytes available.
+func (b *bitReaderBytes) fillFast() {
+ if b.bitsRead < 32 {
+ return
+ }
+
+ // 2 bounds checks.
+ v := b.in[b.off-4 : b.off]
+ v = v[:4]
+ low := (uint32(v[0])) | (uint32(v[1]) << 8) | (uint32(v[2]) << 16) | (uint32(v[3]) << 24)
+ b.value |= uint64(low) << (b.bitsRead - 32)
+ b.bitsRead -= 32
+ b.off -= 4
+}
+
+// fillFastStart() assumes the bitReaderBytes is empty and there is at least 8 bytes to read.
+func (b *bitReaderBytes) fillFastStart() {
+ // Do single re-slice to avoid bounds checks.
+ b.value = binary.LittleEndian.Uint64(b.in[b.off-8:])
+ b.bitsRead = 0
+ b.off -= 8
+}
+
+// fill() will make sure at least 32 bits are available.
+func (b *bitReaderBytes) fill() {
+ if b.bitsRead < 32 {
+ return
+ }
+ if b.off > 4 {
+ v := b.in[b.off-4:]
+ v = v[:4]
+ low := (uint32(v[0])) | (uint32(v[1]) << 8) | (uint32(v[2]) << 16) | (uint32(v[3]) << 24)
+ b.value |= uint64(low) << (b.bitsRead - 32)
+ b.bitsRead -= 32
+ b.off -= 4
+ return
+ }
+ for b.off > 0 {
+ b.value |= uint64(b.in[b.off-1]) << (b.bitsRead - 8)
+ b.bitsRead -= 8
+ b.off--
+ }
+}
+
+// finished returns true if all bits have been read from the bit stream.
+func (b *bitReaderBytes) finished() bool {
+ return b.off == 0 && b.bitsRead >= 64
+}
+
+// close the bitstream and returns an error if out-of-buffer reads occurred.
+func (b *bitReaderBytes) close() error {
+ // Release reference.
+ b.in = nil
+ if b.bitsRead > 64 {
+ return io.ErrUnexpectedEOF
+ }
+ return nil
+}
+
+// bitReaderShifted reads a bitstream in reverse.
+// The last set bit indicates the start of the stream and is used
+// for aligning the input.
+type bitReaderShifted struct {
+ in []byte
+ off uint // next byte to read is at in[off - 1]
+ value uint64
+ bitsRead uint8
+}
+
+// init initializes and resets the bit reader.
+func (b *bitReaderShifted) init(in []byte) error {
+ if len(in) < 1 {
+ return errors.New("corrupt stream: too short")
+ }
+ b.in = in
+ b.off = uint(len(in))
+ // The highest bit of the last byte indicates where to start
+ v := in[len(in)-1]
+ if v == 0 {
+ return errors.New("corrupt stream, did not find end of stream")
+ }
+ b.bitsRead = 64
+ b.value = 0
+ if len(in) >= 8 {
+ b.fillFastStart()
+ } else {
+ b.fill()
+ b.fill()
+ }
+ b.advance(8 - uint8(highBit32(uint32(v))))
+ return nil
+}
+
+// peekBitsFast requires that at least one bit is requested every time.
+// There are no checks if the buffer is filled.
+func (b *bitReaderShifted) peekBitsFast(n uint8) uint16 {
+ return uint16(b.value >> ((64 - n) & 63))
+}
+
+func (b *bitReaderShifted) advance(n uint8) {
+ b.bitsRead += n
+ b.value <<= n & 63
+}
+
+// fillFast() will make sure at least 32 bits are available.
+// There must be at least 4 bytes available.
+func (b *bitReaderShifted) fillFast() {
+ if b.bitsRead < 32 {
+ return
+ }
+
+ // 2 bounds checks.
+ v := b.in[b.off-4 : b.off]
+ v = v[:4]
+ low := (uint32(v[0])) | (uint32(v[1]) << 8) | (uint32(v[2]) << 16) | (uint32(v[3]) << 24)
+ b.value |= uint64(low) << ((b.bitsRead - 32) & 63)
+ b.bitsRead -= 32
+ b.off -= 4
+}
+
+// fillFastStart() assumes the bitReaderShifted is empty and there is at least 8 bytes to read.
+func (b *bitReaderShifted) fillFastStart() {
+ // Do single re-slice to avoid bounds checks.
+ b.value = binary.LittleEndian.Uint64(b.in[b.off-8:])
+ b.bitsRead = 0
+ b.off -= 8
+}
+
+// fill() will make sure at least 32 bits are available.
+func (b *bitReaderShifted) fill() {
+ if b.bitsRead < 32 {
+ return
+ }
+ if b.off > 4 {
+ v := b.in[b.off-4:]
+ v = v[:4]
+ low := (uint32(v[0])) | (uint32(v[1]) << 8) | (uint32(v[2]) << 16) | (uint32(v[3]) << 24)
+ b.value |= uint64(low) << ((b.bitsRead - 32) & 63)
+ b.bitsRead -= 32
+ b.off -= 4
+ return
+ }
+ for b.off > 0 {
+ b.value |= uint64(b.in[b.off-1]) << ((b.bitsRead - 8) & 63)
+ b.bitsRead -= 8
+ b.off--
+ }
+}
+
+// finished returns true if all bits have been read from the bit stream.
+func (b *bitReaderShifted) finished() bool {
+ return b.off == 0 && b.bitsRead >= 64
+}
+
+// close the bitstream and returns an error if out-of-buffer reads occurred.
+func (b *bitReaderShifted) close() error {
+ // Release reference.
+ b.in = nil
+ if b.bitsRead > 64 {
+ return io.ErrUnexpectedEOF
+ }
+ return nil
+}
diff --git a/vendor/github.com/klauspost/compress/huff0/decompress.go b/vendor/github.com/klauspost/compress/huff0/decompress.go
index fb42a398b..a03b2634a 100644
--- a/vendor/github.com/klauspost/compress/huff0/decompress.go
+++ b/vendor/github.com/klauspost/compress/huff0/decompress.go
@@ -25,6 +25,9 @@ type dEntryDouble struct {
len uint8
}
+// Uses special code for all tables that are < 8 bits.
+const use8BitTables = true
+
// ReadTable will read a table from the input.
// The size of the input may be larger than the table definition.
// Any content remaining after the table definition will be returned.
@@ -83,6 +86,7 @@ func ReadTable(in []byte, s *Scratch) (s2 *Scratch, remain []byte, err error) {
}
v2 := v & 15
rankStats[v2]++
+ // (1 << (v2-1)) is slower since the compiler cannot prove that v2 isn't 0.
weightTotal += (1 << v2) >> 1
}
if weightTotal == 0 {
@@ -142,12 +146,14 @@ func ReadTable(in []byte, s *Scratch) (s2 *Scratch, remain []byte, err error) {
d := dEntrySingle{
entry: uint16(s.actualTableLog+1-w) | (uint16(n) << 8),
}
- single := s.dt.single[rankStats[w] : rankStats[w]+length]
+ rank := &rankStats[w]
+ single := s.dt.single[*rank : *rank+length]
for i := range single {
single[i] = d
}
- rankStats[w] += length
+ *rank += length
}
+
return s, in, nil
}
@@ -208,7 +214,10 @@ func (d *Decoder) Decompress1X(dst, src []byte) ([]byte, error) {
if len(d.dt.single) == 0 {
return nil, errors.New("no table loaded")
}
- var br bitReader
+ if use8BitTables && d.actualTableLog <= 8 {
+ return d.decompress1X8Bit(dst, src)
+ }
+ var br bitReaderShifted
err := br.init(src)
if err != nil {
return dst, err
@@ -216,17 +225,6 @@ func (d *Decoder) Decompress1X(dst, src []byte) ([]byte, error) {
maxDecodedSize := cap(dst)
dst = dst[:0]
- decode := func() byte {
- val := br.peekBitsFast(d.actualTableLog) /* note : actualTableLog >= 1 */
- v := d.dt.single[val]
- br.bitsRead += uint8(v.entry)
- return uint8(v.entry >> 8)
- }
- hasDec := func(v dEntrySingle) byte {
- br.bitsRead += uint8(v.entry)
- return uint8(v.entry >> 8)
- }
-
// Avoid bounds check by always having full sized table.
const tlSize = 1 << tableLogMax
const tlMask = tlSize - 1
@@ -238,11 +236,25 @@ func (d *Decoder) Decompress1X(dst, src []byte) ([]byte, error) {
for br.off >= 8 {
br.fillFast()
- buf[off+0] = hasDec(dt[br.peekBitsFast(d.actualTableLog)&tlMask])
- buf[off+1] = hasDec(dt[br.peekBitsFast(d.actualTableLog)&tlMask])
+ v := dt[br.peekBitsFast(d.actualTableLog)&tlMask]
+ br.advance(uint8(v.entry))
+ buf[off+0] = uint8(v.entry >> 8)
+
+ v = dt[br.peekBitsFast(d.actualTableLog)&tlMask]
+ br.advance(uint8(v.entry))
+ buf[off+1] = uint8(v.entry >> 8)
+
+ // Refill
br.fillFast()
- buf[off+2] = hasDec(dt[br.peekBitsFast(d.actualTableLog)&tlMask])
- buf[off+3] = hasDec(dt[br.peekBitsFast(d.actualTableLog)&tlMask])
+
+ v = dt[br.peekBitsFast(d.actualTableLog)&tlMask]
+ br.advance(uint8(v.entry))
+ buf[off+2] = uint8(v.entry >> 8)
+
+ v = dt[br.peekBitsFast(d.actualTableLog)&tlMask]
+ br.advance(uint8(v.entry))
+ buf[off+3] = uint8(v.entry >> 8)
+
off += 4
if off == 0 {
if len(dst)+256 > maxDecodedSize {
@@ -259,13 +271,196 @@ func (d *Decoder) Decompress1X(dst, src []byte) ([]byte, error) {
}
dst = append(dst, buf[:off]...)
- for !br.finished() {
+ // br < 8, so uint8 is fine
+ bitsLeft := uint8(br.off)*8 + 64 - br.bitsRead
+ for bitsLeft > 0 {
br.fill()
+ if false && br.bitsRead >= 32 {
+ if br.off >= 4 {
+ v := br.in[br.off-4:]
+ v = v[:4]
+ low := (uint32(v[0])) | (uint32(v[1]) << 8) | (uint32(v[2]) << 16) | (uint32(v[3]) << 24)
+ br.value = (br.value << 32) | uint64(low)
+ br.bitsRead -= 32
+ br.off -= 4
+ } else {
+ for br.off > 0 {
+ br.value = (br.value << 8) | uint64(br.in[br.off-1])
+ br.bitsRead -= 8
+ br.off--
+ }
+ }
+ }
if len(dst) >= maxDecodedSize {
br.close()
return nil, ErrMaxDecodedSizeExceeded
}
- dst = append(dst, decode())
+ v := d.dt.single[br.peekBitsFast(d.actualTableLog)&tlMask]
+ nBits := uint8(v.entry)
+ br.advance(nBits)
+ bitsLeft -= nBits
+ dst = append(dst, uint8(v.entry>>8))
+ }
+ return dst, br.close()
+}
+
+// decompress1X8Bit will decompress a 1X encoded stream with tablelog <= 8.
+// The cap of the output buffer will be the maximum decompressed size.
+// The length of the supplied input must match the end of a block exactly.
+func (d *Decoder) decompress1X8Bit(dst, src []byte) ([]byte, error) {
+ if d.actualTableLog == 8 {
+ return d.decompress1X8BitExactly(dst, src)
+ }
+ var br bitReaderBytes
+ err := br.init(src)
+ if err != nil {
+ return dst, err
+ }
+ maxDecodedSize := cap(dst)
+ dst = dst[:0]
+
+ // Avoid bounds check by always having full sized table.
+ dt := d.dt.single[:256]
+
+ // Use temp table to avoid bound checks/append penalty.
+ var buf [256]byte
+ var off uint8
+
+ shift := (8 - d.actualTableLog) & 7
+
+ //fmt.Printf("mask: %b, tl:%d\n", mask, d.actualTableLog)
+ for br.off >= 4 {
+ br.fillFast()
+ v := dt[br.peekByteFast()>>shift]
+ br.advance(uint8(v.entry))
+ buf[off+0] = uint8(v.entry >> 8)
+
+ v = dt[br.peekByteFast()>>shift]
+ br.advance(uint8(v.entry))
+ buf[off+1] = uint8(v.entry >> 8)
+
+ v = dt[br.peekByteFast()>>shift]
+ br.advance(uint8(v.entry))
+ buf[off+2] = uint8(v.entry >> 8)
+
+ v = dt[br.peekByteFast()>>shift]
+ br.advance(uint8(v.entry))
+ buf[off+3] = uint8(v.entry >> 8)
+
+ off += 4
+ if off == 0 {
+ if len(dst)+256 > maxDecodedSize {
+ br.close()
+ return nil, ErrMaxDecodedSizeExceeded
+ }
+ dst = append(dst, buf[:]...)
+ }
+ }
+
+ if len(dst)+int(off) > maxDecodedSize {
+ br.close()
+ return nil, ErrMaxDecodedSizeExceeded
+ }
+ dst = append(dst, buf[:off]...)
+
+ // br < 4, so uint8 is fine
+ bitsLeft := int8(uint8(br.off)*8 + (64 - br.bitsRead))
+ for bitsLeft > 0 {
+ if br.bitsRead >= 64-8 {
+ for br.off > 0 {
+ br.value |= uint64(br.in[br.off-1]) << (br.bitsRead - 8)
+ br.bitsRead -= 8
+ br.off--
+ }
+ }
+ if len(dst) >= maxDecodedSize {
+ br.close()
+ return nil, ErrMaxDecodedSizeExceeded
+ }
+ v := dt[br.peekByteFast()>>shift]
+ nBits := uint8(v.entry)
+ br.advance(nBits)
+ bitsLeft -= int8(nBits)
+ dst = append(dst, uint8(v.entry>>8))
+ }
+ return dst, br.close()
+}
+
+// decompress1X8Bit will decompress a 1X encoded stream with tablelog <= 8.
+// The cap of the output buffer will be the maximum decompressed size.
+// The length of the supplied input must match the end of a block exactly.
+func (d *Decoder) decompress1X8BitExactly(dst, src []byte) ([]byte, error) {
+ var br bitReaderBytes
+ err := br.init(src)
+ if err != nil {
+ return dst, err
+ }
+ maxDecodedSize := cap(dst)
+ dst = dst[:0]
+
+ // Avoid bounds check by always having full sized table.
+ dt := d.dt.single[:256]
+
+ // Use temp table to avoid bound checks/append penalty.
+ var buf [256]byte
+ var off uint8
+
+ const shift = 0
+
+ //fmt.Printf("mask: %b, tl:%d\n", mask, d.actualTableLog)
+ for br.off >= 4 {
+ br.fillFast()
+ v := dt[br.peekByteFast()>>shift]
+ br.advance(uint8(v.entry))
+ buf[off+0] = uint8(v.entry >> 8)
+
+ v = dt[br.peekByteFast()>>shift]
+ br.advance(uint8(v.entry))
+ buf[off+1] = uint8(v.entry >> 8)
+
+ v = dt[br.peekByteFast()>>shift]
+ br.advance(uint8(v.entry))
+ buf[off+2] = uint8(v.entry >> 8)
+
+ v = dt[br.peekByteFast()>>shift]
+ br.advance(uint8(v.entry))
+ buf[off+3] = uint8(v.entry >> 8)
+
+ off += 4
+ if off == 0 {
+ if len(dst)+256 > maxDecodedSize {
+ br.close()
+ return nil, ErrMaxDecodedSizeExceeded
+ }
+ dst = append(dst, buf[:]...)
+ }
+ }
+
+ if len(dst)+int(off) > maxDecodedSize {
+ br.close()
+ return nil, ErrMaxDecodedSizeExceeded
+ }
+ dst = append(dst, buf[:off]...)
+
+ // br < 4, so uint8 is fine
+ bitsLeft := int8(uint8(br.off)*8 + (64 - br.bitsRead))
+ for bitsLeft > 0 {
+ if br.bitsRead >= 64-8 {
+ for br.off > 0 {
+ br.value |= uint64(br.in[br.off-1]) << (br.bitsRead - 8)
+ br.bitsRead -= 8
+ br.off--
+ }
+ }
+ if len(dst) >= maxDecodedSize {
+ br.close()
+ return nil, ErrMaxDecodedSizeExceeded
+ }
+ v := dt[br.peekByteFast()>>shift]
+ nBits := uint8(v.entry)
+ br.advance(nBits)
+ bitsLeft -= int8(nBits)
+ dst = append(dst, uint8(v.entry>>8))
}
return dst, br.close()
}
@@ -274,15 +469,18 @@ func (d *Decoder) Decompress1X(dst, src []byte) ([]byte, error) {
// The length of the supplied input must match the end of a block exactly.
// The *capacity* of the dst slice must match the destination size of
// the uncompressed data exactly.
-func (s *Decoder) Decompress4X(dst, src []byte) ([]byte, error) {
- if len(s.dt.single) == 0 {
+func (d *Decoder) Decompress4X(dst, src []byte) ([]byte, error) {
+ if len(d.dt.single) == 0 {
return nil, errors.New("no table loaded")
}
if len(src) < 6+(4*1) {
return nil, errors.New("input too small")
}
+ if use8BitTables && d.actualTableLog <= 8 {
+ return d.decompress4X8bit(dst, src)
+ }
- var br [4]bitReader
+ var br [4]bitReaderShifted
start := 6
for i := 0; i < 3; i++ {
length := int(src[i*2]) | (int(src[i*2+1]) << 8)
@@ -308,14 +506,7 @@ func (s *Decoder) Decompress4X(dst, src []byte) ([]byte, error) {
const tlSize = 1 << tableLogMax
const tlMask = tlSize - 1
- single := s.dt.single[:tlSize]
-
- decode := func(br *bitReader) byte {
- val := br.peekBitsFast(s.actualTableLog) /* note : actualTableLog >= 1 */
- v := single[val&tlMask]
- br.bitsRead += uint8(v.entry)
- return uint8(v.entry >> 8)
- }
+ single := d.dt.single[:tlSize]
// Use temp table to avoid bound checks/append penalty.
var buf [256]byte
@@ -324,69 +515,484 @@ func (s *Decoder) Decompress4X(dst, src []byte) ([]byte, error) {
// Decode 2 values from each decoder/loop.
const bufoff = 256 / 4
-bigloop:
for {
- for i := range br {
- br := &br[i]
- if br.off < 4 {
- break bigloop
- }
- br.fillFast()
+ if br[0].off < 4 || br[1].off < 4 || br[2].off < 4 || br[3].off < 4 {
+ break
}
{
const stream = 0
- val := br[stream].peekBitsFast(s.actualTableLog)
+ const stream2 = 1
+ br[stream].fillFast()
+ br[stream2].fillFast()
+
+ val := br[stream].peekBitsFast(d.actualTableLog)
v := single[val&tlMask]
- br[stream].bitsRead += uint8(v.entry)
+ br[stream].advance(uint8(v.entry))
+ buf[off+bufoff*stream] = uint8(v.entry >> 8)
- val2 := br[stream].peekBitsFast(s.actualTableLog)
+ val2 := br[stream2].peekBitsFast(d.actualTableLog)
v2 := single[val2&tlMask]
- buf[off+bufoff*stream+1] = uint8(v2.entry >> 8)
- buf[off+bufoff*stream] = uint8(v.entry >> 8)
- br[stream].bitsRead += uint8(v2.entry)
+ br[stream2].advance(uint8(v2.entry))
+ buf[off+bufoff*stream2] = uint8(v2.entry >> 8)
+
+ val = br[stream].peekBitsFast(d.actualTableLog)
+ v = single[val&tlMask]
+ br[stream].advance(uint8(v.entry))
+ buf[off+bufoff*stream+1] = uint8(v.entry >> 8)
+
+ val2 = br[stream2].peekBitsFast(d.actualTableLog)
+ v2 = single[val2&tlMask]
+ br[stream2].advance(uint8(v2.entry))
+ buf[off+bufoff*stream2+1] = uint8(v2.entry >> 8)
}
{
- const stream = 1
- val := br[stream].peekBitsFast(s.actualTableLog)
+ const stream = 2
+ const stream2 = 3
+ br[stream].fillFast()
+ br[stream2].fillFast()
+
+ val := br[stream].peekBitsFast(d.actualTableLog)
v := single[val&tlMask]
- br[stream].bitsRead += uint8(v.entry)
+ br[stream].advance(uint8(v.entry))
+ buf[off+bufoff*stream] = uint8(v.entry >> 8)
- val2 := br[stream].peekBitsFast(s.actualTableLog)
+ val2 := br[stream2].peekBitsFast(d.actualTableLog)
v2 := single[val2&tlMask]
- buf[off+bufoff*stream+1] = uint8(v2.entry >> 8)
- buf[off+bufoff*stream] = uint8(v.entry >> 8)
- br[stream].bitsRead += uint8(v2.entry)
+ br[stream2].advance(uint8(v2.entry))
+ buf[off+bufoff*stream2] = uint8(v2.entry >> 8)
+
+ val = br[stream].peekBitsFast(d.actualTableLog)
+ v = single[val&tlMask]
+ br[stream].advance(uint8(v.entry))
+ buf[off+bufoff*stream+1] = uint8(v.entry >> 8)
+
+ val2 = br[stream2].peekBitsFast(d.actualTableLog)
+ v2 = single[val2&tlMask]
+ br[stream2].advance(uint8(v2.entry))
+ buf[off+bufoff*stream2+1] = uint8(v2.entry >> 8)
+ }
+
+ off += 2
+
+ if off == bufoff {
+ if bufoff > dstEvery {
+ return nil, errors.New("corruption detected: stream overrun 1")
+ }
+ copy(out, buf[:bufoff])
+ copy(out[dstEvery:], buf[bufoff:bufoff*2])
+ copy(out[dstEvery*2:], buf[bufoff*2:bufoff*3])
+ copy(out[dstEvery*3:], buf[bufoff*3:bufoff*4])
+ off = 0
+ out = out[bufoff:]
+ decoded += 256
+ // There must at least be 3 buffers left.
+ if len(out) < dstEvery*3 {
+ return nil, errors.New("corruption detected: stream overrun 2")
+ }
+ }
+ }
+ if off > 0 {
+ ioff := int(off)
+ if len(out) < dstEvery*3+ioff {
+ return nil, errors.New("corruption detected: stream overrun 3")
+ }
+ copy(out, buf[:off])
+ copy(out[dstEvery:dstEvery+ioff], buf[bufoff:bufoff*2])
+ copy(out[dstEvery*2:dstEvery*2+ioff], buf[bufoff*2:bufoff*3])
+ copy(out[dstEvery*3:dstEvery*3+ioff], buf[bufoff*3:bufoff*4])
+ decoded += int(off) * 4
+ out = out[off:]
+ }
+
+ // Decode remaining.
+ for i := range br {
+ offset := dstEvery * i
+ br := &br[i]
+ bitsLeft := br.off*8 + uint(64-br.bitsRead)
+ for bitsLeft > 0 {
+ br.fill()
+ if false && br.bitsRead >= 32 {
+ if br.off >= 4 {
+ v := br.in[br.off-4:]
+ v = v[:4]
+ low := (uint32(v[0])) | (uint32(v[1]) << 8) | (uint32(v[2]) << 16) | (uint32(v[3]) << 24)
+ br.value = (br.value << 32) | uint64(low)
+ br.bitsRead -= 32
+ br.off -= 4
+ } else {
+ for br.off > 0 {
+ br.value = (br.value << 8) | uint64(br.in[br.off-1])
+ br.bitsRead -= 8
+ br.off--
+ }
+ }
+ }
+ // end inline...
+ if offset >= len(out) {
+ return nil, errors.New("corruption detected: stream overrun 4")
+ }
+
+ // Read value and increment offset.
+ val := br.peekBitsFast(d.actualTableLog)
+ v := single[val&tlMask].entry
+ nBits := uint8(v)
+ br.advance(nBits)
+ bitsLeft -= uint(nBits)
+ out[offset] = uint8(v >> 8)
+ offset++
+ }
+ decoded += offset - dstEvery*i
+ err = br.close()
+ if err != nil {
+ return nil, err
+ }
+ }
+ if dstSize != decoded {
+ return nil, errors.New("corruption detected: short output block")
+ }
+ return dst, nil
+}
+
+// Decompress4X will decompress a 4X encoded stream.
+// The length of the supplied input must match the end of a block exactly.
+// The *capacity* of the dst slice must match the destination size of
+// the uncompressed data exactly.
+func (d *Decoder) decompress4X8bit(dst, src []byte) ([]byte, error) {
+ if d.actualTableLog == 8 {
+ return d.decompress4X8bitExactly(dst, src)
+ }
+
+ var br [4]bitReaderBytes
+ start := 6
+ for i := 0; i < 3; i++ {
+ length := int(src[i*2]) | (int(src[i*2+1]) << 8)
+ if start+length >= len(src) {
+ return nil, errors.New("truncated input (or invalid offset)")
+ }
+ err := br[i].init(src[start : start+length])
+ if err != nil {
+ return nil, err
+ }
+ start += length
+ }
+ err := br[3].init(src[start:])
+ if err != nil {
+ return nil, err
+ }
+
+ // destination, offset to match first output
+ dstSize := cap(dst)
+ dst = dst[:dstSize]
+ out := dst
+ dstEvery := (dstSize + 3) / 4
+
+ shift := (8 - d.actualTableLog) & 7
+
+ const tlSize = 1 << 8
+ const tlMask = tlSize - 1
+ single := d.dt.single[:tlSize]
+
+ // Use temp table to avoid bound checks/append penalty.
+ var buf [256]byte
+ var off uint8
+ var decoded int
+
+ // Decode 4 values from each decoder/loop.
+ const bufoff = 256 / 4
+ for {
+ if br[0].off < 4 || br[1].off < 4 || br[2].off < 4 || br[3].off < 4 {
+ break
+ }
+
+ {
+ // Interleave 2 decodes.
+ const stream = 0
+ const stream2 = 1
+ br[stream].fillFast()
+ br[stream2].fillFast()
+
+ v := single[br[stream].peekByteFast()>>shift].entry
+ buf[off+bufoff*stream] = uint8(v >> 8)
+ br[stream].advance(uint8(v))
+
+ v2 := single[br[stream2].peekByteFast()>>shift].entry
+ buf[off+bufoff*stream2] = uint8(v2 >> 8)
+ br[stream2].advance(uint8(v2))
+
+ v = single[br[stream].peekByteFast()>>shift].entry
+ buf[off+bufoff*stream+1] = uint8(v >> 8)
+ br[stream].advance(uint8(v))
+
+ v2 = single[br[stream2].peekByteFast()>>shift].entry
+ buf[off+bufoff*stream2+1] = uint8(v2 >> 8)
+ br[stream2].advance(uint8(v2))
+
+ v = single[br[stream].peekByteFast()>>shift].entry
+ buf[off+bufoff*stream+2] = uint8(v >> 8)
+ br[stream].advance(uint8(v))
+
+ v2 = single[br[stream2].peekByteFast()>>shift].entry
+ buf[off+bufoff*stream2+2] = uint8(v2 >> 8)
+ br[stream2].advance(uint8(v2))
+
+ v = single[br[stream].peekByteFast()>>shift].entry
+ buf[off+bufoff*stream+3] = uint8(v >> 8)
+ br[stream].advance(uint8(v))
+
+ v2 = single[br[stream2].peekByteFast()>>shift].entry
+ buf[off+bufoff*stream2+3] = uint8(v2 >> 8)
+ br[stream2].advance(uint8(v2))
}
{
const stream = 2
- val := br[stream].peekBitsFast(s.actualTableLog)
- v := single[val&tlMask]
- br[stream].bitsRead += uint8(v.entry)
+ const stream2 = 3
+ br[stream].fillFast()
+ br[stream2].fillFast()
- val2 := br[stream].peekBitsFast(s.actualTableLog)
- v2 := single[val2&tlMask]
- buf[off+bufoff*stream+1] = uint8(v2.entry >> 8)
- buf[off+bufoff*stream] = uint8(v.entry >> 8)
- br[stream].bitsRead += uint8(v2.entry)
+ v := single[br[stream].peekByteFast()>>shift].entry
+ buf[off+bufoff*stream] = uint8(v >> 8)
+ br[stream].advance(uint8(v))
+
+ v2 := single[br[stream2].peekByteFast()>>shift].entry
+ buf[off+bufoff*stream2] = uint8(v2 >> 8)
+ br[stream2].advance(uint8(v2))
+
+ v = single[br[stream].peekByteFast()>>shift].entry
+ buf[off+bufoff*stream+1] = uint8(v >> 8)
+ br[stream].advance(uint8(v))
+
+ v2 = single[br[stream2].peekByteFast()>>shift].entry
+ buf[off+bufoff*stream2+1] = uint8(v2 >> 8)
+ br[stream2].advance(uint8(v2))
+
+ v = single[br[stream].peekByteFast()>>shift].entry
+ buf[off+bufoff*stream+2] = uint8(v >> 8)
+ br[stream].advance(uint8(v))
+
+ v2 = single[br[stream2].peekByteFast()>>shift].entry
+ buf[off+bufoff*stream2+2] = uint8(v2 >> 8)
+ br[stream2].advance(uint8(v2))
+
+ v = single[br[stream].peekByteFast()>>shift].entry
+ buf[off+bufoff*stream+3] = uint8(v >> 8)
+ br[stream].advance(uint8(v))
+
+ v2 = single[br[stream2].peekByteFast()>>shift].entry
+ buf[off+bufoff*stream2+3] = uint8(v2 >> 8)
+ br[stream2].advance(uint8(v2))
+ }
+
+ off += 4
+
+ if off == bufoff {
+ if bufoff > dstEvery {
+ return nil, errors.New("corruption detected: stream overrun 1")
+ }
+ copy(out, buf[:bufoff])
+ copy(out[dstEvery:], buf[bufoff:bufoff*2])
+ copy(out[dstEvery*2:], buf[bufoff*2:bufoff*3])
+ copy(out[dstEvery*3:], buf[bufoff*3:bufoff*4])
+ off = 0
+ out = out[bufoff:]
+ decoded += 256
+ // There must at least be 3 buffers left.
+ if len(out) < dstEvery*3 {
+ return nil, errors.New("corruption detected: stream overrun 2")
+ }
+ }
+ }
+ if off > 0 {
+ ioff := int(off)
+ if len(out) < dstEvery*3+ioff {
+ return nil, errors.New("corruption detected: stream overrun 3")
+ }
+ copy(out, buf[:off])
+ copy(out[dstEvery:dstEvery+ioff], buf[bufoff:bufoff*2])
+ copy(out[dstEvery*2:dstEvery*2+ioff], buf[bufoff*2:bufoff*3])
+ copy(out[dstEvery*3:dstEvery*3+ioff], buf[bufoff*3:bufoff*4])
+ decoded += int(off) * 4
+ out = out[off:]
+ }
+
+ // Decode remaining.
+ for i := range br {
+ offset := dstEvery * i
+ br := &br[i]
+ bitsLeft := int(br.off*8) + int(64-br.bitsRead)
+ for bitsLeft > 0 {
+ if br.finished() {
+ return nil, io.ErrUnexpectedEOF
+ }
+ if br.bitsRead >= 56 {
+ if br.off >= 4 {
+ v := br.in[br.off-4:]
+ v = v[:4]
+ low := (uint32(v[0])) | (uint32(v[1]) << 8) | (uint32(v[2]) << 16) | (uint32(v[3]) << 24)
+ br.value |= uint64(low) << (br.bitsRead - 32)
+ br.bitsRead -= 32
+ br.off -= 4
+ } else {
+ for br.off > 0 {
+ br.value |= uint64(br.in[br.off-1]) << (br.bitsRead - 8)
+ br.bitsRead -= 8
+ br.off--
+ }
+ }
+ }
+ // end inline...
+ if offset >= len(out) {
+ return nil, errors.New("corruption detected: stream overrun 4")
+ }
+
+ // Read value and increment offset.
+ v := single[br.peekByteFast()>>shift].entry
+ nBits := uint8(v)
+ br.advance(nBits)
+ bitsLeft -= int(nBits)
+ out[offset] = uint8(v >> 8)
+ offset++
+ }
+ decoded += offset - dstEvery*i
+ err = br.close()
+ if err != nil {
+ return nil, err
+ }
+ }
+ if dstSize != decoded {
+ return nil, errors.New("corruption detected: short output block")
+ }
+ return dst, nil
+}
+
+// Decompress4X will decompress a 4X encoded stream.
+// The length of the supplied input must match the end of a block exactly.
+// The *capacity* of the dst slice must match the destination size of
+// the uncompressed data exactly.
+func (d *Decoder) decompress4X8bitExactly(dst, src []byte) ([]byte, error) {
+ var br [4]bitReaderBytes
+ start := 6
+ for i := 0; i < 3; i++ {
+ length := int(src[i*2]) | (int(src[i*2+1]) << 8)
+ if start+length >= len(src) {
+ return nil, errors.New("truncated input (or invalid offset)")
+ }
+ err := br[i].init(src[start : start+length])
+ if err != nil {
+ return nil, err
+ }
+ start += length
+ }
+ err := br[3].init(src[start:])
+ if err != nil {
+ return nil, err
+ }
+
+ // destination, offset to match first output
+ dstSize := cap(dst)
+ dst = dst[:dstSize]
+ out := dst
+ dstEvery := (dstSize + 3) / 4
+
+ const shift = 0
+ const tlSize = 1 << 8
+ const tlMask = tlSize - 1
+ single := d.dt.single[:tlSize]
+
+ // Use temp table to avoid bound checks/append penalty.
+ var buf [256]byte
+ var off uint8
+ var decoded int
+
+ // Decode 4 values from each decoder/loop.
+ const bufoff = 256 / 4
+ for {
+ if br[0].off < 4 || br[1].off < 4 || br[2].off < 4 || br[3].off < 4 {
+ break
}
{
- const stream = 3
- val := br[stream].peekBitsFast(s.actualTableLog)
- v := single[val&tlMask]
- br[stream].bitsRead += uint8(v.entry)
+ // Interleave 2 decodes.
+ const stream = 0
+ const stream2 = 1
+ br[stream].fillFast()
+ br[stream2].fillFast()
- val2 := br[stream].peekBitsFast(s.actualTableLog)
- v2 := single[val2&tlMask]
- buf[off+bufoff*stream+1] = uint8(v2.entry >> 8)
- buf[off+bufoff*stream] = uint8(v.entry >> 8)
- br[stream].bitsRead += uint8(v2.entry)
+ v := single[br[stream].peekByteFast()>>shift].entry
+ buf[off+bufoff*stream] = uint8(v >> 8)
+ br[stream].advance(uint8(v))
+
+ v2 := single[br[stream2].peekByteFast()>>shift].entry
+ buf[off+bufoff*stream2] = uint8(v2 >> 8)
+ br[stream2].advance(uint8(v2))
+
+ v = single[br[stream].peekByteFast()>>shift].entry
+ buf[off+bufoff*stream+1] = uint8(v >> 8)
+ br[stream].advance(uint8(v))
+
+ v2 = single[br[stream2].peekByteFast()>>shift].entry
+ buf[off+bufoff*stream2+1] = uint8(v2 >> 8)
+ br[stream2].advance(uint8(v2))
+
+ v = single[br[stream].peekByteFast()>>shift].entry
+ buf[off+bufoff*stream+2] = uint8(v >> 8)
+ br[stream].advance(uint8(v))
+
+ v2 = single[br[stream2].peekByteFast()>>shift].entry
+ buf[off+bufoff*stream2+2] = uint8(v2 >> 8)
+ br[stream2].advance(uint8(v2))
+
+ v = single[br[stream].peekByteFast()>>shift].entry
+ buf[off+bufoff*stream+3] = uint8(v >> 8)
+ br[stream].advance(uint8(v))
+
+ v2 = single[br[stream2].peekByteFast()>>shift].entry
+ buf[off+bufoff*stream2+3] = uint8(v2 >> 8)
+ br[stream2].advance(uint8(v2))
}
- off += 2
+ {
+ const stream = 2
+ const stream2 = 3
+ br[stream].fillFast()
+ br[stream2].fillFast()
+
+ v := single[br[stream].peekByteFast()>>shift].entry
+ buf[off+bufoff*stream] = uint8(v >> 8)
+ br[stream].advance(uint8(v))
+
+ v2 := single[br[stream2].peekByteFast()>>shift].entry
+ buf[off+bufoff*stream2] = uint8(v2 >> 8)
+ br[stream2].advance(uint8(v2))
+
+ v = single[br[stream].peekByteFast()>>shift].entry
+ buf[off+bufoff*stream+1] = uint8(v >> 8)
+ br[stream].advance(uint8(v))
+
+ v2 = single[br[stream2].peekByteFast()>>shift].entry
+ buf[off+bufoff*stream2+1] = uint8(v2 >> 8)
+ br[stream2].advance(uint8(v2))
+
+ v = single[br[stream].peekByteFast()>>shift].entry
+ buf[off+bufoff*stream+2] = uint8(v >> 8)
+ br[stream].advance(uint8(v))
+
+ v2 = single[br[stream2].peekByteFast()>>shift].entry
+ buf[off+bufoff*stream2+2] = uint8(v2 >> 8)
+ br[stream2].advance(uint8(v2))
+
+ v = single[br[stream].peekByteFast()>>shift].entry
+ buf[off+bufoff*stream+3] = uint8(v >> 8)
+ br[stream].advance(uint8(v))
+
+ v2 = single[br[stream2].peekByteFast()>>shift].entry
+ buf[off+bufoff*stream2+3] = uint8(v2 >> 8)
+ br[stream2].advance(uint8(v2))
+ }
+
+ off += 4
if off == bufoff {
if bufoff > dstEvery {
@@ -422,12 +1028,38 @@ bigloop:
for i := range br {
offset := dstEvery * i
br := &br[i]
- for !br.finished() {
- br.fill()
+ bitsLeft := int(br.off*8) + int(64-br.bitsRead)
+ for bitsLeft > 0 {
+ if br.finished() {
+ return nil, io.ErrUnexpectedEOF
+ }
+ if br.bitsRead >= 56 {
+ if br.off >= 4 {
+ v := br.in[br.off-4:]
+ v = v[:4]
+ low := (uint32(v[0])) | (uint32(v[1]) << 8) | (uint32(v[2]) << 16) | (uint32(v[3]) << 24)
+ br.value |= uint64(low) << (br.bitsRead - 32)
+ br.bitsRead -= 32
+ br.off -= 4
+ } else {
+ for br.off > 0 {
+ br.value |= uint64(br.in[br.off-1]) << (br.bitsRead - 8)
+ br.bitsRead -= 8
+ br.off--
+ }
+ }
+ }
+ // end inline...
if offset >= len(out) {
return nil, errors.New("corruption detected: stream overrun 4")
}
- out[offset] = decode(br)
+
+ // Read value and increment offset.
+ v := single[br.peekByteFast()>>shift].entry
+ nBits := uint8(v)
+ br.advance(nBits)
+ bitsLeft -= int(nBits)
+ out[offset] = uint8(v >> 8)
offset++
}
decoded += offset - dstEvery*i
diff --git a/vendor/github.com/klauspost/compress/zstd/bitreader.go b/vendor/github.com/klauspost/compress/zstd/bitreader.go
index 15d79d439..854458537 100644
--- a/vendor/github.com/klauspost/compress/zstd/bitreader.go
+++ b/vendor/github.com/klauspost/compress/zstd/bitreader.go
@@ -5,6 +5,7 @@
package zstd
import (
+ "encoding/binary"
"errors"
"io"
"math/bits"
@@ -34,8 +35,12 @@ func (b *bitReader) init(in []byte) error {
}
b.bitsRead = 64
b.value = 0
- b.fill()
- b.fill()
+ if len(in) >= 8 {
+ b.fillFastStart()
+ } else {
+ b.fill()
+ b.fill()
+ }
b.bitsRead += 8 - uint8(highBits(uint32(v)))
return nil
}
@@ -63,21 +68,31 @@ func (b *bitReader) fillFast() {
if b.bitsRead < 32 {
return
}
- // Do single re-slice to avoid bounds checks.
- v := b.in[b.off-4 : b.off]
+ // 2 bounds checks.
+ v := b.in[b.off-4:]
+ v = v[:4]
low := (uint32(v[0])) | (uint32(v[1]) << 8) | (uint32(v[2]) << 16) | (uint32(v[3]) << 24)
b.value = (b.value << 32) | uint64(low)
b.bitsRead -= 32
b.off -= 4
}
+// fillFastStart() assumes the bitreader is empty and there is at least 8 bytes to read.
+func (b *bitReader) fillFastStart() {
+ // Do single re-slice to avoid bounds checks.
+ b.value = binary.LittleEndian.Uint64(b.in[b.off-8:])
+ b.bitsRead = 0
+ b.off -= 8
+}
+
// fill() will make sure at least 32 bits are available.
func (b *bitReader) fill() {
if b.bitsRead < 32 {
return
}
if b.off >= 4 {
- v := b.in[b.off-4 : b.off]
+ v := b.in[b.off-4:]
+ v = v[:4]
low := (uint32(v[0])) | (uint32(v[1]) << 8) | (uint32(v[2]) << 16) | (uint32(v[3]) << 24)
b.value = (b.value << 32) | uint64(low)
b.bitsRead -= 32
diff --git a/vendor/github.com/klauspost/compress/zstd/blockdec.go b/vendor/github.com/klauspost/compress/zstd/blockdec.go
index 4a14242c7..c8ec6e331 100644
--- a/vendor/github.com/klauspost/compress/zstd/blockdec.go
+++ b/vendor/github.com/klauspost/compress/zstd/blockdec.go
@@ -83,6 +83,10 @@ type blockDec struct {
err error
decWG sync.WaitGroup
+ // Frame to use for singlethreaded decoding.
+ // Should not be used by the decoder itself since parent may be another frame.
+ localFrame *frameDec
+
// Block is RLE, this is the size.
RLESize uint32
tmp [4]byte
diff --git a/vendor/github.com/klauspost/compress/zstd/bytereader.go b/vendor/github.com/klauspost/compress/zstd/bytereader.go
index f708df1c4..2c4fca17f 100644
--- a/vendor/github.com/klauspost/compress/zstd/bytereader.go
+++ b/vendor/github.com/klauspost/compress/zstd/bytereader.go
@@ -4,8 +4,6 @@
package zstd
-import "encoding/binary"
-
// byteReader provides a byte reader that reads
// little endian values from a byte stream.
// The input stream is manually advanced.
@@ -33,7 +31,8 @@ func (b *byteReader) overread() bool {
// Int32 returns a little endian int32 starting at current offset.
func (b byteReader) Int32() int32 {
- b2 := b.b[b.off : b.off+4 : b.off+4]
+ b2 := b.b[b.off:]
+ b2 = b2[:4]
v3 := int32(b2[3])
v2 := int32(b2[2])
v1 := int32(b2[1])
@@ -57,7 +56,25 @@ func (b byteReader) Uint32() uint32 {
}
return v
}
- return binary.LittleEndian.Uint32(b.b[b.off : b.off+4])
+ b2 := b.b[b.off:]
+ b2 = b2[:4]
+ v3 := uint32(b2[3])
+ v2 := uint32(b2[2])
+ v1 := uint32(b2[1])
+ v0 := uint32(b2[0])
+ return v0 | (v1 << 8) | (v2 << 16) | (v3 << 24)
+}
+
+// Uint32NC returns a little endian uint32 starting at current offset.
+// The caller must be sure if there are at least 4 bytes left.
+func (b byteReader) Uint32NC() uint32 {
+ b2 := b.b[b.off:]
+ b2 = b2[:4]
+ v3 := uint32(b2[3])
+ v2 := uint32(b2[2])
+ v1 := uint32(b2[1])
+ v0 := uint32(b2[0])
+ return v0 | (v1 << 8) | (v2 << 16) | (v3 << 24)
}
// unread returns the unread portion of the input.
diff --git a/vendor/github.com/klauspost/compress/zstd/decoder.go b/vendor/github.com/klauspost/compress/zstd/decoder.go
index 8e34479ff..75bf05bc9 100644
--- a/vendor/github.com/klauspost/compress/zstd/decoder.go
+++ b/vendor/github.com/klauspost/compress/zstd/decoder.go
@@ -23,9 +23,6 @@ type Decoder struct {
// Unreferenced decoders, ready for use.
decoders chan *blockDec
- // Unreferenced decoders, ready for use.
- frames chan *frameDec
-
// Streams ready to be decoded.
stream chan decodeStream
@@ -90,10 +87,10 @@ func NewReader(r io.Reader, opts ...DOption) (*Decoder, error) {
// Create decoders
d.decoders = make(chan *blockDec, d.o.concurrent)
- d.frames = make(chan *frameDec, d.o.concurrent)
for i := 0; i < d.o.concurrent; i++ {
- d.frames <- newFrameDec(d.o)
- d.decoders <- newBlockDec(d.o.lowMem)
+ dec := newBlockDec(d.o.lowMem)
+ dec.localFrame = newFrameDec(d.o)
+ d.decoders <- dec
}
if r == nil {
@@ -283,15 +280,15 @@ func (d *Decoder) DecodeAll(input, dst []byte) ([]byte, error) {
}
// Grab a block decoder and frame decoder.
- block, frame := <-d.decoders, <-d.frames
+ block := <-d.decoders
+ frame := block.localFrame
defer func() {
if debug {
printf("re-adding decoder: %p", block)
}
- d.decoders <- block
frame.rawInput = nil
frame.bBuf = nil
- d.frames <- frame
+ d.decoders <- block
}()
frame.bBuf = input
diff --git a/vendor/github.com/klauspost/compress/zstd/fse_decoder.go b/vendor/github.com/klauspost/compress/zstd/fse_decoder.go
index 957cfeb79..e6d3d49b3 100644
--- a/vendor/github.com/klauspost/compress/zstd/fse_decoder.go
+++ b/vendor/github.com/klauspost/compress/zstd/fse_decoder.go
@@ -55,7 +55,7 @@ func (s *fseDecoder) readNCount(b *byteReader, maxSymbol uint16) error {
if b.remain() < 4 {
return errors.New("input too small")
}
- bitStream := b.Uint32()
+ bitStream := b.Uint32NC()
nbBits := uint((bitStream & 0xF) + minTablelog) // extract tableLog
if nbBits > tablelogAbsoluteMax {
println("Invalid tablelog:", nbBits)
@@ -79,7 +79,8 @@ func (s *fseDecoder) readNCount(b *byteReader, maxSymbol uint16) error {
n0 += 24
if r := b.remain(); r > 5 {
b.advance(2)
- bitStream = b.Uint32() >> bitCount
+ // The check above should make sure we can read 32 bits
+ bitStream = b.Uint32NC() >> bitCount
} else {
// end of bit stream
bitStream >>= 16
@@ -104,10 +105,11 @@ func (s *fseDecoder) readNCount(b *byteReader, maxSymbol uint16) error {
charnum++
}
- if r := b.remain(); r >= 7 || r+int(bitCount>>3) >= 4 {
+ if r := b.remain(); r >= 7 || r-int(bitCount>>3) >= 4 {
b.advance(bitCount >> 3)
bitCount &= 7
- bitStream = b.Uint32() >> bitCount
+ // The check above should make sure we can read 32 bits
+ bitStream = b.Uint32NC() >> bitCount
} else {
bitStream >>= 2
}
@@ -148,17 +150,16 @@ func (s *fseDecoder) readNCount(b *byteReader, maxSymbol uint16) error {
threshold >>= 1
}
- //println("b.off:", b.off, "len:", len(b.b), "bc:", bitCount, "remain:", b.remain())
- if r := b.remain(); r >= 7 || r+int(bitCount>>3) >= 4 {
+ if r := b.remain(); r >= 7 || r-int(bitCount>>3) >= 4 {
b.advance(bitCount >> 3)
bitCount &= 7
+ // The check above should make sure we can read 32 bits
+ bitStream = b.Uint32NC() >> (bitCount & 31)
} else {
bitCount -= (uint)(8 * (len(b.b) - 4 - b.off))
b.off = len(b.b) - 4
- //println("b.off:", b.off, "len:", len(b.b), "bc:", bitCount, "iend", iend)
+ bitStream = b.Uint32() >> (bitCount & 31)
}
- bitStream = b.Uint32() >> (bitCount & 31)
- //printf("bitstream is now: 0b%b", bitStream)
}
s.symbolLen = charnum
if s.symbolLen <= 1 {
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 92d25f428..9f307b650 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -93,7 +93,7 @@ github.com/containers/common/pkg/config
github.com/containers/common/pkg/sysinfo
# github.com/containers/conmon v2.0.18+incompatible
github.com/containers/conmon/runner/config
-# github.com/containers/image/v5 v5.4.5-0.20200529084758-46b2ee6aebb0
+# github.com/containers/image/v5 v5.5.1
github.com/containers/image/v5/copy
github.com/containers/image/v5/directory
github.com/containers/image/v5/directory/explicitfilepath
@@ -324,7 +324,7 @@ github.com/inconshreveable/mousetrap
github.com/ishidawataru/sctp
# github.com/json-iterator/go v1.1.10
github.com/json-iterator/go
-# github.com/klauspost/compress v1.10.7
+# github.com/klauspost/compress v1.10.8
github.com/klauspost/compress/flate
github.com/klauspost/compress/fse
github.com/klauspost/compress/huff0