Size:   l.size,
		Type:   l.mediaType,
		URLS:   l.urls,
	}
}

// initLayerData creates a new layerData object with the given parameters and
// loads the values of the hashes into the layerData object from the given
// diffID and digest files.
func initLayerData(diffIDfile, digestFile, compressedBlob string) (layerData, error) {
	ld := layerData{
		mediaType:      types.DockerLayer,
		compressedBlob: compressedBlob,
		diffIDFile:     diffIDfile,
		digestFile:     digestFile,
	}
	diffID, err := ioutil.ReadFile(ld.diffIDFile)
	if err!= nil {
		return layerData{}, errors.Wrapf(err, "unable to read diffID for layer from %s", ld.diffIDFile)
	}
	ld.diffID = string(diffID)
	digest, err := ioutil.ReadFile(ld.digestFile)
	if err!= nil {
		return layerData{}, errors.Wrapf(err, "unable to read digest for layer from %s", ld.digestFile)
	}
	ld.digest = string(digest)
	return ld, nil
}

// parseTagToFilename converts a list of key=value where 'key' is the name of
// the tagged image and 'value' is the path to a file into a map from key to
// value.
func parseTagToFilename(tags []string, stamper *compat.Stamper) (map[name.Tag]string, error) {
	result := make(map[name.Tag]string)
	for _, t := range tags {
		split := strings.Split(t, "=")
		if len(split)!= 2 {
			return nil, errors.Errorf("%q was not specified in the expected key=value format because it split into %q with unexpected number of elements by '=', got %d, want 2", t, split, len(split))
		}
		img, configFile := split[0], split[1]
		stamped := stamper.Stamp(img)
		parsedTag, err := name.NewTag(stamped, name.WeakValidation)
		if err!= nil {
			return nil, errors.Wrapf(err, "unable to parse stamped image name %q as a fully qualified tagged image name", stamped)
		}
		result[parsedTag] = configFile
	}
	return result, nil
}

// loadImgLayersData adds layerData objects for the given image to the given
// list of layerData and returns the modified list of layersData.
func loadImgLayersData(img v1.Image, layersData []layerData) ([]layerData, error) {
	layers, err := img.Layers()
	if err!= nil {
		return nil, errors.Wrap(err, "unable to get image layers")
	}
	for _, l := range layers {
		// Set the digest & diffID fields in the layerData object because it
		// will be used by uniquifyLayerData to de-duplicate layers and
		// imageLayers to identify what layers belong to the image being built.
		d, err := l.Digest()
		if err!= nil {
			return nil, errors.Wrap(err, "unable to get layer digest")
		}
		diffID, err := l.DiffID()
		if err!= nil {
			return nil, errors.Wrap(err, "unable to get layer diffID")
		}
		layersData = append(layersData, layerData{
			layer:  l,
			digest: d.Hex,
			diffID: diffID.Hex,
		})
	}
	return layersData, nil
}

// uniquifyLayerData removes duplicate layers from the given list of layerData
// objects. Duplicate layers that occur later in the list replace earlier
// occurences. The uniquified list is returned. The given list is not modified.
func uniquifyLayerData(layersData []layerData) []layerData {
	result := []layerData{}
	lookup := make(map[string]bool)
	// Scan in reverse order and reject duplicates found earlier in the list.
	for i := len(layersData) - 1; i >= 0; i-- {
		l := layersData[i]
		_, ok := lookup[l.digest]
		if ok {
			continue
		}
		lookup[l.digest] = true
		result = append(result, l)
	}
	return result
}

// loadLayersData creates layer data objects from the given list of base image
// tarballs and a list of strings where each string has the format
// val1,val2,val3 where:
// val1 is the file containing the layer diffID.
// val2 is the file containing the layer digest.
// val3 is the path to the compressed layer tarball.
func loadLayersData(sourceImages, layers []string) ([]layerData, error) {
	result := []layerData{}
	for _, imgPath := range sourceImages {
		img, err := tarball.ImageFromPath(imgPath, nil)
		if err!= nil {
			return nil, errors.Wrapf(err, "unable to load base image tarball from %s", imgPath)
		}
		result, err = loadImgLayersData(img, result)
		if err!= nil {
			return nil, errors.Wrapf(err, "unable to load layers from base image tarball %s", imgPath)
		}
	}
	for _, l := range layers {
		split := strings.Split(l, ",")
		if len(split)!= 3 {
			return nil, errors.Errorf("%q did not split by ',' into the expected number of elements, got %d, want 4", l, len(split))
		}
		ld, err := initLayerData(split[0], split[1], split[2])
		if err!= nil {
			return nil, errors.Wrap(err, "unable to load layer data")
		}
		result = append(result, ld)
	}
	return uniquifyLayerData(result), nil
}

// buildDiffIDToLayer builds a map from layer diffID to layer data from
// the given list of layer data.
func buildDiffIDToLayer(layersData []layerData) map[string]layerData {
	result := make(map[string]layerData)
	for _, l := range layersData {
		result[l.diffID] = l
	}
	return result
}

// diffIDToLayerFromManifest generates a diffID to layerData lookup from the
// layers in the given base manifest if the manifest defines any layers. The
// manifest is only needed to define layers when the image being built is
// a windows image with a base image with foreign layers.
func diffIDToLayerFromManifest(config *v1.ConfigFile, manifest *v1.Manifest) (map[string]layerData, error) {
	if len(manifest.Layers) == 0 {
		// A manifest was not specified or a blank manifest was specified. This
		// means this image doesn't have any foreign layers so nothing to do
		// here.
		return nil, nil
	}
	// The manifest is from the base image which may have fewer layers than the
	// config. However, the config shouldn't have removed any of the layers from
	// the original base manifest.
	if len(config.RootFS.DiffIDs) < len(manifest.Layers) {
		return nil, errors.Errorf("unexpected number of layers in config %d vs manifest %d, want config to have equal or greater number of layers", len(config.RootFS.DiffIDs), len(manifest.Layers))
	}
	result := make(map[string]layerData)
	// Manifest is only needed to add foreign layers which are a special kind of
	// layer in Windows base images. For every other layer, the layer tarball
	// should be specified with the --layer flag instead.
	for i, l := range manifest.Layers {
		if l.MediaType!= types.DockerForeignLayer {
			continue
		}
		diffID := config.RootFS.DiffIDs[i]
		result[diffID.Hex] = layerData{
			mediaType: types.DockerForeignLayer,
			diffID:    diffID.Hex,
			digest:    l.Digest.Hex,
			size:      l.Size,
			urls:      l.URLs,
		}
	}
	return result, nil
}

// imageLayers returns the layer files from the given layer data lookup for
// an image with the given config