<commit_before>package drivers

import (
	"fmt"
	"io"
	"os"

	"github.com/pkg/errors"

	"github.com/lxc/lxd/lxd/migration"
	"github.com/lxc/lxd/lxd/operations"
	"github.com/lxc/lxd/lxd/revert"
	"github.com/lxc/lxd/lxd/rsync"
	"github.com/lxc/lxd/lxd/storage/quota"
	"github.com/lxc/lxd/shared"
	log "github.com/lxc/lxd/shared/log15"
)

// CreateVolume creates an empty volume and can optionally fill it by executing the supplied
// filler function.
func (d *dir) CreateVolume(vol Volume, filler *VolumeFiller, op *operations.Operation) error {
	volPath := vol.MountPath()

	revert := revert.New()
	defer revert.Fail()

	// Create the volume itself.
	err := vol.EnsureMountPath()
	if err != nil {
		return err
	}
	revert.Add(func() { os.RemoveAll(volPath) })

	// Create sparse loopback file if volume is block.
	rootBlockPath := ""
	if vol.contentType == ContentTypeBlock {
		// We expect the filler to copy the VM image into this path.
		rootBlockPath, err = d.GetVolumeDiskPath(vol)
		if err != nil {
			return err
		}
	} else {
		// Filesystem quotas only used with non-block volume types.
		revertFunc, err := d.setupInitialQuota(vol)
		if err != nil {
			return err
		}

		if revertFunc != nil {
			revert.Add(revertFunc)
		}
	}

	// Run the volume filler function if supplied.
	if filler != nil && filler.Fill != nil {
		d.logger.Debug("Running filler function", log.Ctx{"path": volPath})
		err = filler.Fill(volPath, rootBlockPath)
		if err != nil {
			return err
		}
	}

	// If we are creating a block volume, resize it to the requested size or the default.
	// We expect the filler function to have converted the qcow2 image to raw into the rootBlockPath.
	if vol.contentType == ContentTypeBlock {
		err := ensureVolumeBlockFile(rootBlockPath, vol.ExpandedConfig("size"))
		if err != nil {
			return err
		}

		// Move the GPT alt header to end of disk if needed and if filler specified.
		if vol.IsVMBlock() && filler != nil && filler.Fill != nil {
			err = d.moveGPTAltHeader(rootBlockPath)
			if err != nil {
				return err
			}
		}
	}

	revert.Success()
	return nil
}

// CreateVolumeFromBackup restores a backup tarball onto the storage device.
func (d *dir) CreateVolumeFromBackup(vol Volume, snapshots []string, srcData io.ReadSeeker, optimizedStorage bool, op *operations.Operation) (func(vol Volume) error, func(), error) {
	// Run the generic backup unpacker
	postHook, revertHook, err := genericBackupUnpack(d.withoutGetVolID(), vol, snapshots, srcData, op)
	if err != nil {
		return nil, nil, err
	}

	// Define a post hook function that can be run once the backup config has been restored.
	// This will setup the quota using the restored config.
	postHookWrapper := func(vol Volume) error {
		if postHook != nil {
			err := postHook(vol)
			if err != nil {
				return err
			}
		}

		_, err := d.setupInitialQuota(vol)
		if err != nil {
			return err
		}

		return nil
	}

	return postHookWrapper, revertHook, nil
}

// CreateVolumeFromCopy provides same-pool volume copying functionality.
func (d *dir) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots bool, op *operations.Operation) error {
	var err error
	var srcSnapshots []Volume

	if copySnapshots && !srcVol.IsSnapshot() {
		// Get the list of snapshots from the source.
		srcSnapshots, err = srcVol.Snapshots(op)
		if err != nil {
			return err
		}
	}

	// Run the generic copy.
	return genericCopyVolume(d, d.setupInitialQuota, vol, srcVol, srcSnapshots, false, op)
}

// CreateVolumeFromMigration creates a volume being sent via a migration.
func (d *dir) CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, volTargetArgs migration.VolumeTargetArgs, preFiller *VolumeFiller, op *operations.Operation) error {
	if vol.contentType != ContentTypeFS {
		return ErrNotSupported
	}

	if volTargetArgs.MigrationType.FSType != migration.MigrationFSType_RSYNC {
		return ErrNotSupported
	}

	return genericCreateVolumeFromMigration(d, d.setupInitialQuota, vol, conn, volTargetArgs, preFiller, op)
}

// RefreshVolume provides same-pool volume and specific snapshots syncing functionality.
func (d *dir) RefreshVolume(vol Volume, srcVol Volume, srcSnapshots []Volume, op *operations.Operation) error {
	return genericCopyVolume(d, d.setupInitialQuota, vol, srcVol, srcSnapshots, true, op)
}

// DeleteVolume deletes a volume of the storage device. If any snapshots of the volume remain then
// this function will return an error.
func (d *dir) DeleteVolume(vol Volume, op *operations.Operation) error {
	snapshots, err := d.VolumeSnapshots(vol, op)
	if err != nil {
		return err
	}

	if len(snapshots) > 0 {
		return fmt.Errorf("Cannot remove a volume that has snapshots")
	}

	volPath := vol.MountPath()

	// If the volume doesn't exist, then nothing more to do.
	if !shared.PathExists(volPath) {
		return nil
	}

	// Get the volume ID for the volume, which is used to remove project quota.
	volID, err := d.getVolID(vol.volType, vol.name)
	if err != nil {
		return err
	}

	// Remove the project quota.
	err = d.deleteQuota(volPath, volID)
	if err != nil {
		return err
	}

	// Remove the volume from the storage device.
	err = os.RemoveAll(volPath)
	if err != nil && !os.IsNotExist(err) {
		return errors.Wrapf(err, "Failed to remove '%s'", volPath)
	}

	// Although the volume snapshot directory should already be removed, lets remove it here
	// to just in case the top-level directory is left.
	err = deleteParentSnapshotDirIfEmpty(d.name, vol.volType, vol.name)
	if err != nil {
		return err
	}

	return nil
}

// HasVolume indicates whether a specific volume exists on the storage pool.
func (d *dir) HasVolume(vol Volume) bool {
	return genericVFSHasVolume(vol)
}

// ValidateVolume validates the supplied volume config. Optionally removes invalid keys from the volume's config.
func (d *dir) ValidateVolume(vol Volume, removeUnknownKeys bool) error {
	return d.validateVolume(vol, nil, removeUnknownKeys)
}

// UpdateVolume applies config changes to the volume.
func (d *dir) UpdateVolume(vol Volume, changedConfig map[string]string) error {
	if vol.contentType != ContentTypeFS {
		return ErrNotSupported
	}

	if _, changed := changedConfig["size"]; changed {
		err := d.SetVolumeQuota(vol, changedConfig["size"], nil)
		if err != nil {
			return err
		}
	}

	return nil
}

// GetVolumeUsage returns the disk space used by the volume.
func (d *dir) GetVolumeUsage(vol Volume) (int64, error) {
	volPath := vol.MountPath()
	ok, err := quota.Supported(volPath)
	if err != nil || !ok {
		return 0, nil
	}

	// Get the volume ID for the volume to access quota.
	volID, err := d.getVolID(vol.volType, vol.name)
	if err != nil {
		return -1, err
	}

	projectID := d.quotaProjectID(volID)

	// Get project quota used.
	size, err := quota.GetProjectUsage(volPath, projectID)
	if err != nil {
		return -1, err
	}

	return size, nil
}

// SetVolumeQuota sets the quota on the volume.
func (d *dir) SetVolumeQuota(vol Volume, size string, op *operations.Operation) error {
	// For VM block files, resize the file if needed.
	if vol.contentType == ContentTypeBlock {
		rootBlockPath, err := d.GetVolumeDiskPath(vol)
		if err != nil {
			return err
		}

		// If size not specified in volume config, then use pool's default block size.
		if size == "" || size == "0" {
			size = defaultBlockSize
		}

		resized, err := genericVFSResizeBlockFile(rootBlockPath, size)
		if err != nil {
			return err
		}

		// Move the GPT alt header to end of disk if needed and resize has taken place.
		if vol.IsVMBlock() && resized {
			err = d.moveGPTAltHeader(rootBlockPath)
			if err != nil {
				return err
			}
		}

		return nil
	}

	// For non-VM block volumes, set filesystem quota.
	volID, err := d.getVolID(vol.volType, vol.name)
	if err != nil {
		return err
	}

	return d.setQuota(vol.MountPath(), volID, size)
}

// GetVolumeDiskPath returns the location of a disk volume.
func (d *dir) GetVolumeDiskPath(vol Volume) (string, error) {
	return genericVFSGetVolumeDiskPath(vol)
}

// MountVolume simulates mounting a volume. As dir driver doesn't have volumes to mount it returns
// false indicating that there is no need to issue an unmount.
func (d *dir) MountVolume(vol Volume, op *operations.Operation) (bool, error) {
	return false, nil
}

// UnmountVolume simulates unmounting a volume. As dir driver doesn't have volumes to unmount it
// returns false indicating the volume was already unmounted.
func (d *dir) UnmountVolume(vol Volume, op *operations.Operation) (bool, error) {
	return false, nil
}

// RenameVolume renames a volume and its snapshots.
func (d *dir) RenameVolume(vol Volume, newVolName string, op *operations.Operation) error {
	return genericVFSRenameVolume(d, vol, newVolName, op)
}

// MigrateVolume sends a volume for migration.
func (d *dir) MigrateVolume(vol Volume, conn io.ReadWriteCloser, volSrcArgs *migration.VolumeSourceArgs, op *operations.Operation) error {
	if vol.contentType != ContentTypeFS {
		return ErrNotSupported
	}

	if volSrcArgs.MigrationType.FSType != migration.MigrationFSType_RSYNC {
		return ErrNotSupported
	}

	return genericVFSMigrateVolume(d, d.state, vol, conn, volSrcArgs, op)
}

// BackupVolume copies a volume (and optionally its snapshots) to a specified target path.
// This driver does not support optimized backups.
func (d *dir) BackupVolume(vol Volume, targetPath string, optimized bool, snapshots bool, op *operations.Operation) error {
	return genericVFSBackupVolume(d, vol, targetPath, snapshots, op)
}

// CreateVolumeSnapshot creates a snapshot of a volume.
func (d *dir) CreateVolumeSnapshot(snapVol Volume, op *operations.Operation) error {
	parentName, _, _ := shared.InstanceGetParentAndSnapshotName(snapVol.name)
	srcPath := GetVolumeMountPath(d.name, snapVol.volType, parentName)
	snapPath := snapVol.MountPath()

	// Create the parent directory.
	err := createParentSnapshotDirIfMissing(d.name, snapVol.volType, parentName)
	if err != nil {
		return err
	}

	// Create snapshot directory.
	err = snapVol.EnsureMountPath()
	if err != nil {
		return err
	}

	revertPath := true
	defer func() {
		if revertPath {
			os.RemoveAll(snapPath)
		}
	}()

	bwlimit := d.config["rsync.bwlimit"]

	// Copy volume into snapshot directory.
	_, err = rsync.LocalCopy(srcPath, snapPath, bwlimit, true)
	if err != nil {
		return err
	}

	revertPath = false
	return nil
}

// DeleteVolumeSnapshot removes a snapshot from the storage device. The volName and snapshotName
// must be bare names and should not be in the format "volume/snapshot".
func (d *dir) DeleteVolumeSnapshot(snapVol Volume, op *operations.Operation) error {
	snapPath := snapVol.MountPath()

	// Remove the snapshot from the storage device.
	err := os.RemoveAll(snapPath)
	if err != nil && !os.IsNotExist(err) {
		return errors.Wrapf(err, "Failed to remove '%s'", snapPath)
	}

	parentName, _, _ := shared.InstanceGetParentAndSnapshotName(snapVol.name)

	// Remove the parent snapshot directory if this is the last snapshot being removed.
	err = deleteParentSnapshotDirIfEmpty(d.name, snapVol.volType, parentName)
	if err != nil {
		return err
	}

	return nil
}

// MountVolumeSnapshot sets up a read-only mount on top of the snapshot to avoid accidental modifications.
func (d *dir) MountVolumeSnapshot(snapVol Volume, op *operations.Operation) (bool, error) {
	snapPath := snapVol.MountPath()
	return mountReadOnly(snapPath, snapPath)
}

// UnmountVolumeSnapshot removes the read-only mount placed on top of a snapshot.
func (d *dir) UnmountVolumeSnapshot(snapVol Volume, op *operations.Operation) (bool, error) {
	snapPath := snapVol.MountPath()
	return forceUnmount(snapPath)
}

// VolumeSnapshots returns a list of snapshots for the volume.
func (d *dir) VolumeSnapshots(vol Volume, op *operations.Operation) ([]string, error) {
	return genericVFSVolumeSnapshots(d, vol, op)
}

// RestoreVolume restores a volume from a snapshot.
func (d *dir) RestoreVolume(vol Volume, snapshotName string, op *operations.Operation) error {
	srcPath := GetVolumeMountPath(d.name, vol.volType, GetSnapshotVolumeName(vol.name, snapshotName))
	if !shared.PathExists(srcPath) {
		return fmt.Errorf("Snapshot not found")
	}

	volPath := vol.MountPath()

	// Restore using rsync.
	bwlimit := d.config["rsync.bwlimit"]
	_, err := rsync.LocalCopy(srcPath, volPath, bwlimit, true)
	if err != nil {
		return errors.Wrap(err, "Failed to rsync volume")
	}

	return nil
}

// RenameVolumeSnapshot renames a volume snapshot.
func (d *dir) RenameVolumeSnapshot(snapVol Volume, newSnapshotName string, op *operations.Operation) error {
	return genericVFSRenameVolumeSnapshot(d, snapVol, newSnapshotName, op)
}
<commit_msg>lxd/storage/drivers/driver/dir/volumes: Updates migration to support VMs<commit_after>package drivers

import (
	"fmt"
	"io"
	"os"

	"github.com/pkg/errors"

	"github.com/lxc/lxd/lxd/migration"
	"github.com/lxc/lxd/lxd/operations"
	"github.com/lxc/lxd/lxd/revert"
	"github.com/lxc/lxd/lxd/rsync"
	"github.com/lxc/lxd/lxd/storage/quota"
	"github.com/lxc/lxd/shared"
	log "github.com/lxc/lxd/shared/log15"
)

// CreateVolume creates an empty volume and can optionally fill it by executing the supplied
// filler function.
func (d *dir) CreateVolume(vol Volume, filler *VolumeFiller, op *operations.Operation) error {
	volPath := vol.MountPath()

	revert := revert.New()
	defer revert.Fail()

	// Create the volume itself.
	err := vol.EnsureMountPath()
	if err != nil {
		return err
	}
	revert.Add(func() { os.RemoveAll(volPath) })

	// Create sparse loopback file if volume is block.
	rootBlockPath := ""
	if vol.contentType == ContentTypeBlock {
		// We expect the filler to copy the VM image into this path.
		rootBlockPath, err = d.GetVolumeDiskPath(vol)
		if err != nil {
			return err
		}
	} else {
		// Filesystem quotas only used with non-block volume types.
		revertFunc, err := d.setupInitialQuota(vol)
		if err != nil {
			return err
		}

		if revertFunc != nil {
			revert.Add(revertFunc)
		}
	}

	// Run the volume filler function if supplied.
	if filler != nil && filler.Fill != nil {
		d.logger.Debug("Running filler function", log.Ctx{"path": volPath})
		err = filler.Fill(volPath, rootBlockPath)
		if err != nil {
			return err
		}
	}

	// If we are creating a block volume, resize it to the requested size or the default.
	// We expect the filler function to have converted the qcow2 image to raw into the rootBlockPath.
	if vol.contentType == ContentTypeBlock {
		err := ensureVolumeBlockFile(rootBlockPath, vol.ExpandedConfig("size"))
		if err != nil {
			return err
		}

		// Move the GPT alt header to end of disk if needed and if filler specified.
		if vol.IsVMBlock() && filler != nil && filler.Fill != nil {
			err = d.moveGPTAltHeader(rootBlockPath)
			if err != nil {
				return err
			}
		}
	}

	revert.Success()
	return nil
}

// CreateVolumeFromBackup restores a backup tarball onto the storage device.
func (d *dir) CreateVolumeFromBackup(vol Volume, snapshots []string, srcData io.ReadSeeker, optimizedStorage bool, op *operations.Operation) (func(vol Volume) error, func(), error) {
	// Run the generic backup unpacker
	postHook, revertHook, err := genericBackupUnpack(d.withoutGetVolID(), vol, snapshots, srcData, op)
	if err != nil {
		return nil, nil, err
	}

	// Define a post hook function that can be run once the backup config has been restored.
	// This will setup the quota using the restored config.
	postHookWrapper := func(vol Volume) error {
		if postHook != nil {
			err := postHook(vol)
			if err != nil {
				return err
			}
		}

		_, err := d.setupInitialQuota(vol)
		if err != nil {
			return err
		}

		return nil
	}

	return postHookWrapper, revertHook, nil
}

// CreateVolumeFromCopy provides same-pool volume copying functionality.
func (d *dir) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots bool, op *operations.Operation) error {
	var err error
	var srcSnapshots []Volume

	if copySnapshots && !srcVol.IsSnapshot() {
		// Get the list of snapshots from the source.
		srcSnapshots, err = srcVol.Snapshots(op)
		if err != nil {
			return err
		}
	}

	// Run the generic copy.
	return genericCopyVolume(d, d.setupInitialQuota, vol, srcVol, srcSnapshots, false, op)
}

// CreateVolumeFromMigration creates a volume being sent via a migration.
func (d *dir) CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, volTargetArgs migration.VolumeTargetArgs, preFiller *VolumeFiller, op *operations.Operation) error {
	return genericCreateVolumeFromMigration(d, d.setupInitialQuota, vol, conn, volTargetArgs, preFiller, op)
}

// RefreshVolume provides same-pool volume and specific snapshots syncing functionality.
func (d *dir) RefreshVolume(vol Volume, srcVol Volume, srcSnapshots []Volume, op *operations.Operation) error {
	return genericCopyVolume(d, d.setupInitialQuota, vol, srcVol, srcSnapshots, true, op)
}

// DeleteVolume deletes a volume of the storage device. If any snapshots of the volume remain then
// this function will return an error.
func (d *dir) DeleteVolume(vol Volume, op *operations.Operation) error {
	snapshots, err := d.VolumeSnapshots(vol, op)
	if err != nil {
		return err
	}

	if len(snapshots) > 0 {
		return fmt.Errorf("Cannot remove a volume that has snapshots")
	}

	volPath := vol.MountPath()

	// If the volume doesn't exist, then nothing more to do.
	if !shared.PathExists(volPath) {
		return nil
	}

	// Get the volume ID for the volume, which is used to remove project quota.
	volID, err := d.getVolID(vol.volType, vol.name)
	if err != nil {
		return err
	}

	// Remove the project quota.
	err = d.deleteQuota(volPath, volID)
	if err != nil {
		return err
	}

	// Remove the volume from the storage device.
	err = os.RemoveAll(volPath)
	if err != nil && !os.IsNotExist(err) {
		return errors.Wrapf(err, "Failed to remove '%s'", volPath)
	}

	// Although the volume snapshot directory should already be removed, lets remove it here
	// to just in case the top-level directory is left.
	err = deleteParentSnapshotDirIfEmpty(d.name, vol.volType, vol.name)
	if err != nil {
		return err
	}

	return nil
}

// HasVolume indicates whether a specific volume exists on the storage pool.
func (d *dir) HasVolume(vol Volume) bool {
	return genericVFSHasVolume(vol)
}

// ValidateVolume validates the supplied volume config. Optionally removes invalid keys from the volume's config.
func (d *dir) ValidateVolume(vol Volume, removeUnknownKeys bool) error {
	return d.validateVolume(vol, nil, removeUnknownKeys)
}

// UpdateVolume applies config changes to the volume.
func (d *dir) UpdateVolume(vol Volume, changedConfig map[string]string) error {
	if vol.contentType != ContentTypeFS {
		return ErrNotSupported
	}

	if _, changed := changedConfig["size"]; changed {
		err := d.SetVolumeQuota(vol, changedConfig["size"], nil)
		if err != nil {
			return err
		}
	}

	return nil
}

// GetVolumeUsage returns the disk space used by the volume.
func (d *dir) GetVolumeUsage(vol Volume) (int64, error) {
	volPath := vol.MountPath()
	ok, err := quota.Supported(volPath)
	if err != nil || !ok {
		return 0, nil
	}

	// Get the volume ID for the volume to access quota.
	volID, err := d.getVolID(vol.volType, vol.name)
	if err != nil {
		return -1, err
	}

	projectID := d.quotaProjectID(volID)

	// Get project quota used.
	size, err := quota.GetProjectUsage(volPath, projectID)
	if err != nil {
		return -1, err
	}

	return size, nil
}

// SetVolumeQuota sets the quota on the volume.
func (d *dir) SetVolumeQuota(vol Volume, size string, op *operations.Operation) error {
	// For VM block files, resize the file if needed.
	if vol.contentType == ContentTypeBlock {
		rootBlockPath, err := d.GetVolumeDiskPath(vol)
		if err != nil {
			return err
		}

		// If size not specified in volume config, then use pool's default block size.
		if size == "" || size == "0" {
			size = defaultBlockSize
		}

		resized, err := genericVFSResizeBlockFile(rootBlockPath, size)
		if err != nil {
			return err
		}

		// Move the GPT alt header to end of disk if needed and resize has taken place.
		if vol.IsVMBlock() && resized {
			err = d.moveGPTAltHeader(rootBlockPath)
			if err != nil {
				return err
			}
		}

		return nil
	}

	// For non-VM block volumes, set filesystem quota.
	volID, err := d.getVolID(vol.volType, vol.name)
	if err != nil {
		return err
	}

	return d.setQuota(vol.MountPath(), volID, size)
}

// GetVolumeDiskPath returns the location of a disk volume.
func (d *dir) GetVolumeDiskPath(vol Volume) (string, error) {
	return genericVFSGetVolumeDiskPath(vol)
}

// MountVolume simulates mounting a volume. As dir driver doesn't have volumes to mount it returns
// false indicating that there is no need to issue an unmount.
func (d *dir) MountVolume(vol Volume, op *operations.Operation) (bool, error) {
	return false, nil
}

// UnmountVolume simulates unmounting a volume. As dir driver doesn't have volumes to unmount it
// returns false indicating the volume was already unmounted.
func (d *dir) UnmountVolume(vol Volume, op *operations.Operation) (bool, error) {
	return false, nil
}

// RenameVolume renames a volume and its snapshots.
func (d *dir) RenameVolume(vol Volume, newVolName string, op *operations.Operation) error {
	return genericVFSRenameVolume(d, vol, newVolName, op)
}

// MigrateVolume sends a volume for migration.
func (d *dir) MigrateVolume(vol Volume, conn io.ReadWriteCloser, volSrcArgs *migration.VolumeSourceArgs, op *operations.Operation) error {
	return genericVFSMigrateVolume(d, d.state, vol, conn, volSrcArgs, op)
}

// BackupVolume copies a volume (and optionally its snapshots) to a specified target path.
// This driver does not support optimized backups.
func (d *dir) BackupVolume(vol Volume, targetPath string, optimized bool, snapshots bool, op *operations.Operation) error {
	return genericVFSBackupVolume(d, vol, targetPath, snapshots, op)
}

// CreateVolumeSnapshot creates a snapshot of a volume.
func (d *dir) CreateVolumeSnapshot(snapVol Volume, op *operations.Operation) error {
	parentName, _, _ := shared.InstanceGetParentAndSnapshotName(snapVol.name)
	srcPath := GetVolumeMountPath(d.name, snapVol.volType, parentName)
	snapPath := snapVol.MountPath()

	// Create the parent directory.
	err := createParentSnapshotDirIfMissing(d.name, snapVol.volType, parentName)
	if err != nil {
		return err
	}

	// Create snapshot directory.
	err = snapVol.EnsureMountPath()
	if err != nil {
		return err
	}

	revertPath := true
	defer func() {
		if revertPath {
			os.RemoveAll(snapPath)
		}
	}()

	bwlimit := d.config["rsync.bwlimit"]

	// Copy volume into snapshot directory.
	_, err = rsync.LocalCopy(srcPath, snapPath, bwlimit, true)
	if err != nil {
		return err
	}

	revertPath = false
	return nil
}

// DeleteVolumeSnapshot removes a snapshot from the storage device. The volName and snapshotName
// must be bare names and should not be in the format "volume/snapshot".
func (d *dir) DeleteVolumeSnapshot(snapVol Volume, op *operations.Operation) error {
	snapPath := snapVol.MountPath()

	// Remove the snapshot from the storage device.
	err := os.RemoveAll(snapPath)
	if err != nil && !os.IsNotExist(err) {
		return errors.Wrapf(err, "Failed to remove '%s'", snapPath)
	}

	parentName, _, _ := shared.InstanceGetParentAndSnapshotName(snapVol.name)

	// Remove the parent snapshot directory if this is the last snapshot being removed.
	err = deleteParentSnapshotDirIfEmpty(d.name, snapVol.volType, parentName)
	if err != nil {
		return err
	}

	return nil
}

// MountVolumeSnapshot sets up a read-only mount on top of the snapshot to avoid accidental modifications.
func (d *dir) MountVolumeSnapshot(snapVol Volume, op *operations.Operation) (bool, error) {
	snapPath := snapVol.MountPath()
	return mountReadOnly(snapPath, snapPath)
}

// UnmountVolumeSnapshot removes the read-only mount placed on top of a snapshot.
func (d *dir) UnmountVolumeSnapshot(snapVol Volume, op *operations.Operation) (bool, error) {
	snapPath := snapVol.MountPath()
	return forceUnmount(snapPath)
}

// VolumeSnapshots returns a list of snapshots for the volume.
func (d *dir) VolumeSnapshots(vol Volume, op *operations.Operation) ([]string, error) {
	return genericVFSVolumeSnapshots(d, vol, op)
}

// RestoreVolume restores a volume from a snapshot.
func (d *dir) RestoreVolume(vol Volume, snapshotName string, op *operations.Operation) error {
	srcPath := GetVolumeMountPath(d.name, vol.volType, GetSnapshotVolumeName(vol.name, snapshotName))
	if !shared.PathExists(srcPath) {
		return fmt.Errorf("Snapshot not found")
	}

	volPath := vol.MountPath()

	// Restore using rsync.
	bwlimit := d.config["rsync.bwlimit"]
	_, err := rsync.LocalCopy(srcPath, volPath, bwlimit, true)
	if err != nil {
		return errors.Wrap(err, "Failed to rsync volume")
	}

	return nil
}

// RenameVolumeSnapshot renames a volume snapshot.
func (d *dir) RenameVolumeSnapshot(snapVol Volume, newSnapshotName string, op *operations.Operation) error {
	return genericVFSRenameVolumeSnapshot(d, snapVol, newSnapshotName, op)
}
<|endoftext|>


<commit_before>package server

import "github.com/labstack/echo"

func RegisterController(name string, e *echo.Echo, m []echo.Middleware, dal DataAccessLayer, config Config) {
	rc := NewResourceController(name, dal)
	rcBase := e.Group("/" + name)
	rcBase.Get("", rc.IndexHandler)
	rcBase.Post("", rc.CreateHandler)
	rcBase.Delete("", rc.ConditionalDeleteHandler)

	rcItem := rcBase.Group("/:id")
	rcItem.Get("", rc.ShowHandler)
	rcItem.Put("", rc.UpdateHandler)
	rcItem.Delete("", rc.DeleteHandler)

	if len(m) > 0 {
		rcBase.Use(m...)
	}

	if config.UseSmartAuth {
		rcBase.Use(SmartAuthHandler(name))
	}
}

func RegisterRoutes(e *echo.Echo, config map[string][]echo.Middleware, dal DataAccessLayer, serverConfig Config) {
	// Batch Support
	batch := NewBatchController(dal)
	e.Post("/", batch.Post)

	// Resource, useSmartAuths
	RegisterController("Appointment", e, config["Appointment"], dal, serverConfig)
	RegisterController("ReferralRequest", e, config["ReferralRequest"], dal, serverConfig)
	RegisterController("Account", e, config["Account"], dal, serverConfig)
	RegisterController("Provenance", e, config["Provenance"], dal, serverConfig)
	RegisterController("Questionnaire", e, config["Questionnaire"], dal, serverConfig)
	RegisterController("ExplanationOfBenefit", e, config["ExplanationOfBenefit"], dal, serverConfig)
	RegisterController("DocumentManifest", e, config["DocumentManifest"], dal, serverConfig)
	RegisterController("Specimen", e, config["Specimen"], dal, serverConfig)
	RegisterController("AllergyIntolerance", e, config["AllergyIntolerance"], dal, serverConfig)
	RegisterController("CarePlan", e, config["CarePlan"], dal, serverConfig)
	RegisterController("Goal", e, config["Goal"], dal, serverConfig)
	RegisterController("StructureDefinition", e, config["StructureDefinition"], dal, serverConfig)
	RegisterController("EnrollmentRequest", e, config["EnrollmentRequest"], dal, serverConfig)
	RegisterController("EpisodeOfCare", e, config["EpisodeOfCare"], dal, serverConfig)
	RegisterController("OperationOutcome", e, config["OperationOutcome"], dal, serverConfig)
	RegisterController("Medication", e, config["Medication"], dal, serverConfig)
	RegisterController("Procedure", e, config["Procedure"], dal, serverConfig)
	RegisterController("List", e, config["List"], dal, serverConfig)
	RegisterController("ConceptMap", e, config["ConceptMap"], dal, serverConfig)
	RegisterController("Subscription", e, config["Subscription"], dal, serverConfig)
	RegisterController("ValueSet", e, config["ValueSet"], dal, serverConfig)
	RegisterController("OperationDefinition", e, config["OperationDefinition"], dal, serverConfig)
	RegisterController("DocumentReference", e, config["DocumentReference"], dal, serverConfig)
	RegisterController("Order", e, config["Order"], dal, serverConfig)
	RegisterController("Immunization", e, config["Immunization"], dal, serverConfig)
	RegisterController("Device", e, config["Device"], dal, serverConfig)
	RegisterController("VisionPrescription", e, config["VisionPrescription"], dal, serverConfig)
	RegisterController("Media", e, config["Media"], dal, serverConfig)
	RegisterController("Conformance", e, config["Conformance"], dal, serverConfig)
	RegisterController("ProcedureRequest", e, config["ProcedureRequest"], dal, serverConfig)
	RegisterController("EligibilityResponse", e, config["EligibilityResponse"], dal, serverConfig)
	RegisterController("DeviceUseRequest", e, config["DeviceUseRequest"], dal, serverConfig)
	RegisterController("DeviceMetric", e, config["DeviceMetric"], dal, serverConfig)
	RegisterController("Flag", e, config["Flag"], dal, serverConfig)
	RegisterController("RelatedPerson", e, config["RelatedPerson"], dal, serverConfig)
	RegisterController("SupplyRequest", e, config["SupplyRequest"], dal, serverConfig)
	RegisterController("Practitioner", e, config["Practitioner"], dal, serverConfig)
	RegisterController("AppointmentResponse", e, config["AppointmentResponse"], dal, serverConfig)
	RegisterController("Observation", e, config["Observation"], dal, serverConfig)
	RegisterController("MedicationAdministration", e, config["MedicationAdministration"], dal, serverConfig)
	RegisterController("Slot", e, config["Slot"], dal, serverConfig)
	RegisterController("EnrollmentResponse", e, config["EnrollmentResponse"], dal, serverConfig)
	RegisterController("Binary", e, config["Binary"], dal, serverConfig)
	RegisterController("MedicationStatement", e, config["MedicationStatement"], dal, serverConfig)
	RegisterController("Person", e, config["Person"], dal, serverConfig)
	RegisterController("Contract", e, config["Contract"], dal, serverConfig)
	RegisterController("CommunicationRequest", e, config["CommunicationRequest"], dal, serverConfig)
	RegisterController("RiskAssessment", e, config["RiskAssessment"], dal, serverConfig)
	RegisterController("TestScript", e, config["TestScript"], dal, serverConfig)
	RegisterController("Basic", e, config["Basic"], dal, serverConfig)
	RegisterController("Group", e, config["Group"], dal, serverConfig)
	RegisterController("PaymentNotice", e, config["PaymentNotice"], dal, serverConfig)
	RegisterController("Organization", e, config["Organization"], dal, serverConfig)
	RegisterController("ImplementationGuide", e, config["ImplementationGuide"], dal, serverConfig)
	RegisterController("ClaimResponse", e, config["ClaimResponse"], dal, serverConfig)
	RegisterController("EligibilityRequest", e, config["EligibilityRequest"], dal, serverConfig)
	RegisterController("ProcessRequest", e, config["ProcessRequest"], dal, serverConfig)
	RegisterController("MedicationDispense", e, config["MedicationDispense"], dal, serverConfig)
	RegisterController("DiagnosticReport", e, config["DiagnosticReport"], dal, serverConfig)
	RegisterController("ImagingStudy", e, config["ImagingStudy"], dal, serverConfig)
	RegisterController("ImagingObjectSelection", e, config["ImagingObjectSelection"], dal, serverConfig)
	RegisterController("HealthcareService", e, config["HealthcareService"], dal, serverConfig)
	RegisterController("DataElement", e, config["DataElement"], dal, serverConfig)
	RegisterController("DeviceComponent", e, config["DeviceComponent"], dal, serverConfig)
	RegisterController("FamilyMemberHistory", e, config["FamilyMemberHistory"], dal, serverConfig)
	RegisterController("NutritionOrder", e, config["NutritionOrder"], dal, serverConfig)
	RegisterController("Encounter", e, config["Encounter"], dal, serverConfig)
	RegisterController("Substance", e, config["Substance"], dal, serverConfig)
	RegisterController("AuditEvent", e, config["AuditEvent"], dal, serverConfig)
	RegisterController("MedicationOrder", e, config["MedicationOrder"], dal, serverConfig)
	RegisterController("SearchParameter", e, config["SearchParameter"], dal, serverConfig)
	RegisterController("PaymentReconciliation", e, config["PaymentReconciliation"], dal, serverConfig)
	RegisterController("Communication", e, config["Communication"], dal, serverConfig)
	RegisterController("Condition", e, config["Condition"], dal, serverConfig)
	RegisterController("Composition", e, config["Composition"], dal, serverConfig)
	RegisterController("DetectedIssue", e, config["DetectedIssue"], dal, serverConfig)
	RegisterController("Bundle", e, config["Bundle"], dal, serverConfig)
	RegisterController("DiagnosticOrder", e, config["DiagnosticOrder"], dal, serverConfig)
	RegisterController("Patient", e, config["Patient"], dal, serverConfig)
	RegisterController("OrderResponse", e, config["OrderResponse"], dal, serverConfig)
	RegisterController("Coverage", e, config["Coverage"], dal, serverConfig)
	RegisterController("QuestionnaireResponse", e, config["QuestionnaireResponse"], dal, serverConfig)
	RegisterController("DeviceUseStatement", e, config["DeviceUseStatement"], dal, serverConfig)
	RegisterController("ProcessResponse", e, config["ProcessResponse"], dal, serverConfig)
	RegisterController("NamingSystem", e, config["NamingSystem"], dal, serverConfig)
	RegisterController("Schedule", e, config["Schedule"], dal, serverConfig)
	RegisterController("SupplyDelivery", e, config["SupplyDelivery"], dal, serverConfig)
	RegisterController("ClinicalImpression", e, config["ClinicalImpression"], dal, serverConfig)
	RegisterController("MessageHeader", e, config["MessageHeader"], dal, serverConfig)
	RegisterController("Claim", e, config["Claim"], dal, serverConfig)
	RegisterController("ImmunizationRecommendation", e, config["ImmunizationRecommendation"], dal, serverConfig)
	RegisterController("Location", e, config["Location"], dal, serverConfig)
	RegisterController("BodySite", e, config["BodySite"], dal, serverConfig)
}
<commit_msg>Minor tweak to comment to match generator output<commit_after>package server

import "github.com/labstack/echo"

func RegisterController(name string, e *echo.Echo, m []echo.Middleware, dal DataAccessLayer, config Config) {
	rc := NewResourceController(name, dal)
	rcBase := e.Group("/" + name)
	rcBase.Get("", rc.IndexHandler)
	rcBase.Post("", rc.CreateHandler)
	rcBase.Delete("", rc.ConditionalDeleteHandler)

	rcItem := rcBase.Group("/:id")
	rcItem.Get("", rc.ShowHandler)
	rcItem.Put("", rc.UpdateHandler)
	rcItem.Delete("", rc.DeleteHandler)

	if len(m) > 0 {
		rcBase.Use(m...)
	}

	if config.UseSmartAuth {
		rcBase.Use(SmartAuthHandler(name))
	}
}

func RegisterRoutes(e *echo.Echo, config map[string][]echo.Middleware, dal DataAccessLayer, serverConfig Config) {

	// Batch Support
	batch := NewBatchController(dal)
	e.Post("/", batch.Post)

	// Resources

	RegisterController("Appointment", e, config["Appointment"], dal, serverConfig)
	RegisterController("ReferralRequest", e, config["ReferralRequest"], dal, serverConfig)
	RegisterController("Account", e, config["Account"], dal, serverConfig)
	RegisterController("Provenance", e, config["Provenance"], dal, serverConfig)
	RegisterController("Questionnaire", e, config["Questionnaire"], dal, serverConfig)
	RegisterController("ExplanationOfBenefit", e, config["ExplanationOfBenefit"], dal, serverConfig)
	RegisterController("DocumentManifest", e, config["DocumentManifest"], dal, serverConfig)
	RegisterController("Specimen", e, config["Specimen"], dal, serverConfig)
	RegisterController("AllergyIntolerance", e, config["AllergyIntolerance"], dal, serverConfig)
	RegisterController("CarePlan", e, config["CarePlan"], dal, serverConfig)
	RegisterController("Goal", e, config["Goal"], dal, serverConfig)
	RegisterController("StructureDefinition", e, config["StructureDefinition"], dal, serverConfig)
	RegisterController("EnrollmentRequest", e, config["EnrollmentRequest"], dal, serverConfig)
	RegisterController("EpisodeOfCare", e, config["EpisodeOfCare"], dal, serverConfig)
	RegisterController("OperationOutcome", e, config["OperationOutcome"], dal, serverConfig)
	RegisterController("Medication", e, config["Medication"], dal, serverConfig)
	RegisterController("Procedure", e, config["Procedure"], dal, serverConfig)
	RegisterController("List", e, config["List"], dal, serverConfig)
	RegisterController("ConceptMap", e, config["ConceptMap"], dal, serverConfig)
	RegisterController("Subscription", e, config["Subscription"], dal, serverConfig)
	RegisterController("ValueSet", e, config["ValueSet"], dal, serverConfig)
	RegisterController("OperationDefinition", e, config["OperationDefinition"], dal, serverConfig)
	RegisterController("DocumentReference", e, config["DocumentReference"], dal, serverConfig)
	RegisterController("Order", e, config["Order"], dal, serverConfig)
	RegisterController("Immunization", e, config["Immunization"], dal, serverConfig)
	RegisterController("Device", e, config["Device"], dal, serverConfig)
	RegisterController("VisionPrescription", e, config["VisionPrescription"], dal, serverConfig)
	RegisterController("Media", e, config["Media"], dal, serverConfig)
	RegisterController("Conformance", e, config["Conformance"], dal, serverConfig)
	RegisterController("ProcedureRequest", e, config["ProcedureRequest"], dal, serverConfig)
	RegisterController("EligibilityResponse", e, config["EligibilityResponse"], dal, serverConfig)
	RegisterController("DeviceUseRequest", e, config["DeviceUseRequest"], dal, serverConfig)
	RegisterController("DeviceMetric", e, config["DeviceMetric"], dal, serverConfig)
	RegisterController("Flag", e, config["Flag"], dal, serverConfig)
	RegisterController("RelatedPerson", e, config["RelatedPerson"], dal, serverConfig)
	RegisterController("SupplyRequest", e, config["SupplyRequest"], dal, serverConfig)
	RegisterController("Practitioner", e, config["Practitioner"], dal, serverConfig)
	RegisterController("AppointmentResponse", e, config["AppointmentResponse"], dal, serverConfig)
	RegisterController("Observation", e, config["Observation"], dal, serverConfig)
	RegisterController("MedicationAdministration", e, config["MedicationAdministration"], dal, serverConfig)
	RegisterController("Slot", e, config["Slot"], dal, serverConfig)
	RegisterController("EnrollmentResponse", e, config["EnrollmentResponse"], dal, serverConfig)
	RegisterController("Binary", e, config["Binary"], dal, serverConfig)
	RegisterController("MedicationStatement", e, config["MedicationStatement"], dal, serverConfig)
	RegisterController("Person", e, config["Person"], dal, serverConfig)
	RegisterController("Contract", e, config["Contract"], dal, serverConfig)
	RegisterController("CommunicationRequest", e, config["CommunicationRequest"], dal, serverConfig)
	RegisterController("RiskAssessment", e, config["RiskAssessment"], dal, serverConfig)
	RegisterController("TestScript", e, config["TestScript"], dal, serverConfig)
	RegisterController("Basic", e, config["Basic"], dal, serverConfig)
	RegisterController("Group", e, config["Group"], dal, serverConfig)
	RegisterController("PaymentNotice", e, config["PaymentNotice"], dal, serverConfig)
	RegisterController("Organization", e, config["Organization"], dal, serverConfig)
	RegisterController("ImplementationGuide", e, config["ImplementationGuide"], dal, serverConfig)
	RegisterController("ClaimResponse", e, config["ClaimResponse"], dal, serverConfig)
	RegisterController("EligibilityRequest", e, config["EligibilityRequest"], dal, serverConfig)
	RegisterController("ProcessRequest", e, config["ProcessRequest"], dal, serverConfig)
	RegisterController("MedicationDispense", e, config["MedicationDispense"], dal, serverConfig)
	RegisterController("DiagnosticReport", e, config["DiagnosticReport"], dal, serverConfig)
	RegisterController("ImagingStudy", e, config["ImagingStudy"], dal, serverConfig)
	RegisterController("ImagingObjectSelection", e, config["ImagingObjectSelection"], dal, serverConfig)
	RegisterController("HealthcareService", e, config["HealthcareService"], dal, serverConfig)
	RegisterController("DataElement", e, config["DataElement"], dal, serverConfig)
	RegisterController("DeviceComponent", e, config["DeviceComponent"], dal, serverConfig)
	RegisterController("FamilyMemberHistory", e, config["FamilyMemberHistory"], dal, serverConfig)
	RegisterController("NutritionOrder", e, config["NutritionOrder"], dal, serverConfig)
	RegisterController("Encounter", e, config["Encounter"], dal, serverConfig)
	RegisterController("Substance", e, config["Substance"], dal, serverConfig)
	RegisterController("AuditEvent", e, config["AuditEvent"], dal, serverConfig)
	RegisterController("MedicationOrder", e, config["MedicationOrder"], dal, serverConfig)
	RegisterController("SearchParameter", e, config["SearchParameter"], dal, serverConfig)
	RegisterController("PaymentReconciliation", e, config["PaymentReconciliation"], dal, serverConfig)
	RegisterController("Communication", e, config["Communication"], dal, serverConfig)
	RegisterController("Condition", e, config["Condition"], dal, serverConfig)
	RegisterController("Composition", e, config["Composition"], dal, serverConfig)
	RegisterController("DetectedIssue", e, config["DetectedIssue"], dal, serverConfig)
	RegisterController("Bundle", e, config["Bundle"], dal, serverConfig)
	RegisterController("DiagnosticOrder", e, config["DiagnosticOrder"], dal, serverConfig)
	RegisterController("Patient", e, config["Patient"], dal, serverConfig)
	RegisterController("OrderResponse", e, config["OrderResponse"], dal, serverConfig)
	RegisterController("Coverage", e, config["Coverage"], dal, serverConfig)
	RegisterController("QuestionnaireResponse", e, config["QuestionnaireResponse"], dal, serverConfig)
	RegisterController("DeviceUseStatement", e, config["DeviceUseStatement"], dal, serverConfig)
	RegisterController("ProcessResponse", e, config["ProcessResponse"], dal, serverConfig)
	RegisterController("NamingSystem", e, config["NamingSystem"], dal, serverConfig)
	RegisterController("Schedule", e, config["Schedule"], dal, serverConfig)
	RegisterController("SupplyDelivery", e, config["SupplyDelivery"], dal, serverConfig)
	RegisterController("ClinicalImpression", e, config["ClinicalImpression"], dal, serverConfig)
	RegisterController("MessageHeader", e, config["MessageHeader"], dal, serverConfig)
	RegisterController("Claim", e, config["Claim"], dal, serverConfig)
	RegisterController("ImmunizationRecommendation", e, config["ImmunizationRecommendation"], dal, serverConfig)
	RegisterController("Location", e, config["Location"], dal, serverConfig)
	RegisterController("BodySite", e, config["BodySite"], dal, serverConfig)
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package bigquery

import (
	"fmt"

	"golang.org/x/net/context"
	"google.golang.org/api/iterator"
)

// A pageFetcher returns a page of rows, starting from the row specified by token.
type pageFetcher interface {
	fetch(ctx context.Context, s service, token string) (*readDataResult, error)
	setPaging(*pagingConf)
}

func newRowIterator(ctx context.Context, s service, pf pageFetcher) *RowIterator {
	it := &RowIterator{
		ctx:     ctx,
		service: s,
		pf:      pf,
	}
	it.pageInfo, it.nextFunc = iterator.NewPageInfo(
		it.fetch,
		func() int { return len(it.rows) },
		func() interface{} { r := it.rows; it.rows = nil; return r })
	return it
}

// A RowIterator provides access to the result of a BigQuery lookup.
type RowIterator struct {
	ctx      context.Context
	service  service
	pf       pageFetcher
	pageInfo *iterator.PageInfo
	nextFunc func() error

	// StartIndex can be set before the first call to Next. If PageInfo().PageToken
	// is also set, StartIndex is ignored.
	StartIndex uint64

	rows [][]Value

	schema Schema // populated on first call to fetch
}

// Next loads the next row into dst. Its return value is iterator.Done if there
// are no more results. Once Next returns iterator.Done, all subsequent calls
// will return iterator.Done.
//
// dst may implement ValueLoader, or may be of type *[]Value
// or *map[string]Value.
func (it *RowIterator) Next(dst interface{}) error {
	vl, ok := dst.(ValueLoader)
	if !ok {
		switch dst := dst.(type) {
		case *[]Value:
			vl = (*ValueList)(dst)
		case *map[string]Value:
			vl = (*valueMap)(dst)
		default:
			return fmt.Errorf("bigquery: cannot convert %T to ValueLoader", dst)
		}
	}
	if err := it.nextFunc(); err != nil {
		return err
	}
	row := it.rows[0]
	it.rows = it.rows[1:]
	return vl.Load(row, it.schema)
}

// PageInfo supports pagination. See the google.golang.org/api/iterator package for details.
func (it *RowIterator) PageInfo() *iterator.PageInfo { return it.pageInfo }

func (it *RowIterator) fetch(pageSize int, pageToken string) (string, error) {
	pc := &pagingConf{}
	if pageSize > 0 {
		pc.recordsPerRequest = int64(pageSize)
		pc.setRecordsPerRequest = true
	}
	if pageToken == "" {
		pc.startIndex = it.StartIndex
	}
	it.pf.setPaging(pc)
	var res *readDataResult
	var err error
	for {
		res, err = it.pf.fetch(it.ctx, it.service, pageToken)
		if err != errIncompleteJob {
			break
		}
	}
	if err != nil {
		return "", err
	}
	it.rows = append(it.rows, res.rows...)
	it.schema = res.schema
	return res.pageToken, nil
}
<commit_msg>bigquery: fix doc, PageInfo.PageToken is now PageInfo.Token<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package bigquery

import (
	"fmt"

	"golang.org/x/net/context"
	"google.golang.org/api/iterator"
)

// A pageFetcher returns a page of rows, starting from the row specified by token.
type pageFetcher interface {
	fetch(ctx context.Context, s service, token string) (*readDataResult, error)
	setPaging(*pagingConf)
}

func newRowIterator(ctx context.Context, s service, pf pageFetcher) *RowIterator {
	it := &RowIterator{
		ctx:     ctx,
		service: s,
		pf:      pf,
	}
	it.pageInfo, it.nextFunc = iterator.NewPageInfo(
		it.fetch,
		func() int { return len(it.rows) },
		func() interface{} { r := it.rows; it.rows = nil; return r })
	return it
}

// A RowIterator provides access to the result of a BigQuery lookup.
type RowIterator struct {
	ctx      context.Context
	service  service
	pf       pageFetcher
	pageInfo *iterator.PageInfo
	nextFunc func() error

	// StartIndex can be set before the first call to Next. If PageInfo().Token
	// is also set, StartIndex is ignored.
	StartIndex uint64

	rows [][]Value

	schema Schema // populated on first call to fetch
}

// Next loads the next row into dst. Its return value is iterator.Done if there
// are no more results. Once Next returns iterator.Done, all subsequent calls
// will return iterator.Done.
//
// dst may implement ValueLoader, or may be of type *[]Value
// or *map[string]Value.
func (it *RowIterator) Next(dst interface{}) error {
	vl, ok := dst.(ValueLoader)
	if !ok {
		switch dst := dst.(type) {
		case *[]Value:
			vl = (*ValueList)(dst)
		case *map[string]Value:
			vl = (*valueMap)(dst)
		default:
			return fmt.Errorf("bigquery: cannot convert %T to ValueLoader", dst)
		}
	}
	if err := it.nextFunc(); err != nil {
		return err
	}
	row := it.rows[0]
	it.rows = it.rows[1:]
	return vl.Load(row, it.schema)
}

// PageInfo supports pagination. See the google.golang.org/api/iterator package for details.
func (it *RowIterator) PageInfo() *iterator.PageInfo { return it.pageInfo }

func (it *RowIterator) fetch(pageSize int, pageToken string) (string, error) {
	pc := &pagingConf{}
	if pageSize > 0 {
		pc.recordsPerRequest = int64(pageSize)
		pc.setRecordsPerRequest = true
	}
	if pageToken == "" {
		pc.startIndex = it.StartIndex
	}
	it.pf.setPaging(pc)
	var res *readDataResult
	var err error
	for {
		res, err = it.pf.fetch(it.ctx, it.service, pageToken)
		if err != errIncompleteJob {
			break
		}
	}
	if err != nil {
		return "", err
	}
	it.rows = append(it.rows, res.rows...)
	it.schema = res.schema
	return res.pageToken, nil
}
<|endoftext|>


<commit_before>package server

import (
	"log"
	"path/filepath"
	"time"

	"github.com/fsnotify/fsnotify"
)

func (s *Server) watchFiles() error {
	var (
		site      = s.Site
		events    = make(chan string)
		debounced = debounce(time.Second, events)
	)

	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		return err
	}

	go func() {
		for {
			select {
			case event := <-watcher.Events:
				events <- event.Name
			case err := <-watcher.Errors:
				log.Println("error:", err)
			}
		}
	}()

	go func() {
		for {
			filenames := <-debounced
			// Resolve to URLS *before* reloading the site, in case the latter
			// remaps permalinks.
			urls := map[string]bool{}
			for _, filename := range filenames {
				relpath, err := filepath.Rel(site.Source, filename)
				if err != nil {
					log.Println("error:", err)
					continue
				}
				url, found := site.RelativeFilenameToURL(relpath)
				if !found {
					// TODO don't warn re config and excluded files
					log.Println("error:", filename, "does not match a site URL")
				}
				urls[url] = true
			}
			s.reloadSite()
			for url := range urls {
				s.lr.Reload(url)
			}
		}
	}()

	return watcher.Add(site.Source)
}

// debounce relays values from input to output, merging successive values within interval
// TODO consider https://github.com/ReactiveX/RxGo
func debounce(interval time.Duration, input chan string) (output chan []string) {
	output = make(chan []string)
	var (
		pending = []string{}
		ticker  = time.Tick(interval) // nolint: staticcheck, megacheck
	)
	go func() {
		for {
			select {
			case value := <-input:
				pending = append(pending, value)
			case <-ticker:
				if len(pending) > 0 {
					output <- pending
					pending = []string{}
				}
			}
		}
	}()
	return
}
<commit_msg>Add <- to channel types<commit_after>package server

import (
	"log"
	"path/filepath"
	"time"

	"github.com/fsnotify/fsnotify"
)

func (s *Server) watchFiles() error {
	var (
		site      = s.Site
		events    = make(chan string)
		debounced = debounce(time.Second, events)
	)

	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		return err
	}

	go func() {
		for {
			select {
			case event := <-watcher.Events:
				events <- event.Name
			case err := <-watcher.Errors:
				log.Println("error:", err)
			}
		}
	}()

	go func() {
		for {
			filenames := <-debounced
			// Resolve to URLS *before* reloading the site, in case the latter
			// remaps permalinks.
			urls := map[string]bool{}
			for _, filename := range filenames {
				relpath, err := filepath.Rel(site.Source, filename)
				if err != nil {
					log.Println("error:", err)
					continue
				}
				url, found := site.RelativeFilenameToURL(relpath)
				if !found {
					// TODO don't warn re config and excluded files
					log.Println("error:", filename, "does not match a site URL")
				}
				urls[url] = true
			}
			s.reloadSite()
			for url := range urls {
				s.lr.Reload(url)
			}
		}
	}()

	return watcher.Add(site.Source)
}

// debounce relays values from input to output, merging successive values within interval
// TODO consider https://github.com/ReactiveX/RxGo
func debounce(interval time.Duration, input <-chan string) <-chan []string {
	output := make(chan []string)
	var (
		pending = []string{}
		ticker  = time.Tick(interval) // nolint: staticcheck, megacheck
	)
	go func() {
		for {
			select {
			case value := <-input:
				pending = append(pending, value)
			case <-ticker:
				if len(pending) > 0 {
					output <- pending
					pending = []string{}
				}
			}
		}
	}()
	return output
}
<|endoftext|>


<commit_before>// Copyright (c) 2014 The SurgeMQ Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package service

import (
	"bufio"
	"fmt"
	"io"
	"sync/atomic"
	//"github.com/surgemq/message"
	"encoding/binary"
	"math"
	"sync"
)

var (
	bufcnt            int64
	DefaultBufferSize int64

	DeviceInBufferSize  int64
	DeviceOutBufferSize int64

	MasterInBufferSize  int64
	MasterOutBufferSize int64
)

const (
	smallReadBlockSize    = 512
	defaultReadBlockSize  = 8192
	defaultWriteBlockSize = 8192
)

/**
2016.03.03 修改
bingbuffer结构体
*/
type buffer struct {
	readIndex  int64        //读序号
	writeIndex int64        //写序号
	ringBuffer []*ByteArray //环形buffer指针数组
	bufferSize int64        //初始化环形buffer指针数组大小
	mask       int64        //掩码：bufferSize-1
	done       int64        //是否完成
	rcond      *sync.Cond
	wcond      *sync.Cond
}

type ByteArray struct {
	bArray []byte
}

func (this *buffer) ReadCommit(index int64) {
	this.rcond.L.Lock()
	defer this.rcond.L.Unlock()
	this.ringBuffer[index] = nil
	this.rcond.Broadcast()
	this.wcond.Broadcast()
}

func (this *buffer) Len() int {
	cpos := this.GetCurrentReadIndex()
	ppos := this.GetCurrentWriteIndex()
	return int(ppos - cpos)
}

/**
2016.03.03 添加
初始化ringbuffer
参数bufferSize：初始化环形buffer指针数组大小
*/
func newBuffer(size int64) (*buffer, error) {
	if size < 0 {
		return nil, bufio.ErrNegativeCount
	}
	if size == 0 {
		size = DefaultBufferSize
	}
	if !powerOfTwo64(size) {
		fmt.Printf("Size must be power of two. Try %d.", roundUpPowerOfTwo64(size))
		return nil, fmt.Errorf("Size must be power of two. Try %d.", roundUpPowerOfTwo64(size))
	}

	return &buffer{
		readIndex:  int64(0),                 //读序号
		writeIndex: int64(0),                 //写序号
		ringBuffer: make([]*ByteArray, size), //环形buffer指针数组
		bufferSize: size,                     //初始化环形buffer指针数组大小
		mask:       size - 1,
		rcond:      sync.NewCond(new(sync.Mutex)),
		wcond:      sync.NewCond(new(sync.Mutex)),
	}, nil
}

/**
2016.03.03 添加
获取当前读序号
*/
func (this *buffer) GetCurrentReadIndex() int64 {
	return atomic.LoadInt64(&this.readIndex)
}

/**
2016.03.03 添加
获取当前写序号
*/
func (this *buffer) GetCurrentWriteIndex() int64 {
	return atomic.LoadInt64(&this.writeIndex)
}

/**
2016.03.03 添加
读取ringbuffer指定的buffer指针，返回该指针并清空ringbuffer该位置存在的指针内容，以及将读序号加1
*/
func (this *buffer) ReadBuffer() ([]byte, int64, bool) {
	this.rcond.L.Lock()
	defer this.rcond.L.Unlock()

	for {
		readIndex := atomic.LoadInt64(&this.readIndex)
		writeIndex := atomic.LoadInt64(&this.writeIndex)
		switch {
		case readIndex >= writeIndex:
			this.rcond.Wait()
		case writeIndex-readIndex > this.bufferSize:
			this.rcond.Wait()
		default:
			if readIndex == math.MaxInt64 {
				atomic.StoreInt64(&this.readIndex, int64(0))
			} else {
				atomic.AddInt64(&this.readIndex, int64(1))
			}

			index := readIndex & this.mask

			p_ := this.ringBuffer[index]
			//this.ringBuffer[index] = nil
			if p_ == nil {
				return nil, -1, false
			}
			p := p_.bArray

			if p == nil {
				return nil, -1, false
			}

			return p, index, true
		}
	}

}

/**
2016.03.03 添加
写入ringbuffer指针，以及将写序号加1
*/
func (this *buffer) WriteBuffer(in []byte) bool {
	this.wcond.L.Lock()
	defer this.wcond.L.Unlock()

	for {
		readIndex := atomic.LoadInt64(&this.readIndex)
		writeIndex := atomic.LoadInt64(&this.writeIndex)
		switch {
		case writeIndex-readIndex < 0:
			this.wcond.Wait()
		default:
			index := writeIndex & this.mask
			if writeIndex == math.MaxInt64 {
				atomic.StoreInt64(&this.writeIndex, int64(0))
			} else {
				atomic.AddInt64(&this.writeIndex, int64(1))
			}
			if this.ringBuffer[index] == nil {
				this.ringBuffer[index] = &ByteArray{bArray: in}
				this.rcond.Broadcast()
				return true
			} else {
				this.rcond.Broadcast()
				return false
			}
		}
	}

}

/**
2016.03.03 修改
完成
*/
func (this *buffer) Close() error {
	//atomic.StoreInt64(&this.done, 1)

	this.wcond.L.Lock()
	this.rcond.Broadcast()
	this.wcond.L.Unlock()

	this.rcond.L.Lock()
	this.wcond.Broadcast()
	this.rcond.L.Unlock()

	return nil
}

/*

/**
2016.03.03 修改
向ringbuffer中写数据（从connection的中向ringbuffer中写）--生产者
*/
func (this *buffer) ReadFrom(r io.Reader) (int64, error) {
	defer this.Close()
	total := int64(0)
	//for {

	//if this.isDone() {
	//	return total, io.EOF
	//}
	b := make([]byte, int64(5))
	n, err := r.Read(b[0:1])

	if n > 0 {
		total += int64(n)
		if err != nil {
			return total, err
		}
	}

	/**************************/
	cnt := 1

	// Let's read enough bytes to get the message header (msg type, remaining length)
	for {
		// If we have read 5 bytes and still not done, then there's a problem.
		if cnt > 4 {
			return 0, fmt.Errorf("sendrecv/peekMessageSize: 4th byte of remaining length has continuation bit set")
		}

		Log.Infoc(func() string {
			return fmt.Sprintf("sendrecv/peekMessageSize: %d=========", cnt)
		})
		// Peek cnt bytes from the input buffer.

		_, err := r.Read(b[cnt:(cnt + 1)])
		//fmt.Println(b)
		if err != nil {
			return 0, err
		}
		// If we got enough bytes, then check the last byte to see if the continuation
		// bit is set. If so, increment cnt and continue peeking
		if b[cnt] >= 0x80 {
			cnt++
		} else {
			break
		}
	}

	// Get the remaining length of the message
	remlen, m := binary.Uvarint(b[1:])
	//Log.Infoc(func() string {
	//	return fmt.Sprintf("b[cnt:(cnt + 1)]==end")
	//})
	// Total message length is remlen + 1 (msg type) + m (remlen bytes)

	total = int64(remlen) + int64(1) + int64(m)
	//Log.Infoc(func() string {
	//	return fmt.Sprintf("remlen===n===totle: %d===%d===%d", remlen, m, total)
	//})
	//mtype := message.MessageType(b[0] >> 4)
	/****************/
	//var msg message.Message
	//
	//msg, err = mtype.New()
	//if err != nil {
	//	return 0, err
	//}
	b_ := make([]byte, int64(remlen))
	_, err = r.Read(b_[0:])
	if err != nil {
		fmt.Println("写入buffer失败，total：%d", total)
		return total, err
	}
	fmt.Println(b_)
	b = append(b[int64(1) + int64(m):], b_[0:]...)
	fmt.Println(b)
	//n, err = msg.Decode(b)
	//if err != nil {
	//	return 0, err
	//}

	/*************************/

	if !this.WriteBuffer(b) {
		return total, err
	}

	return total, nil
	//}
}

/**
2016.03.03 修改
*/
func (this *buffer) WriteTo(w io.Writer) (int64, error) {
	defer this.Close()
	total := int64(0)
	//for {
	//if this.isDone() {
	//	return total, io.EOF
	//}
	p, index, ok := this.ReadBuffer()
	defer this.ReadCommit(index)
	if !ok {
		return total, io.EOF
	}

	Log.Debugc(func() string {
		return fmt.Sprintf("defer this.ReadCommit(%s)", index)
	})
	Log.Debugc(func() string {
		return fmt.Sprintf("WriteTo函数》》读取*p：" + string(p))
	})

	Log.Debugc(func() string {
		return fmt.Sprintf(" WriteTo(w io.Writer)(7)")
	})
	//
	//Log.Errorc(func() string {
	//	return fmt.Sprintf("msg::" + msg.Name())
	//})
	//
	//p := make([]byte, msg.Len())
	//_, err := msg.Encode(p)
	//if err != nil {
	//	Log.Errorc(func() string {
	//		return fmt.Sprintf("msg.Encode(p)")
	//	})
	//	return total, io.EOF
	//}
	// There's some data, let's process it first
	if len(p) > 0 {
		n, err := w.Write(p)
		total += int64(n)
		Log.Debugc(func() string {
			return fmt.Sprintf("Wrote %d bytes, totaling %d bytes", n, total)
		})

		if err != nil {
			Log.Errorc(func() string {
				return fmt.Sprintf("w.Write(p) error")
			})
			return total, err
		}
	}

	return total, nil
	//}
}

/**
2016.03.03 修改
*/
func (this *buffer) isDone() bool {
	//if atomic.LoadInt64(&this.done) == 1 {
	//	return true
	//}

	return false
}

func powerOfTwo64(n int64) bool {
	return n != 0 && (n&(n-1)) == 0
}

func roundUpPowerOfTwo64(n int64) int64 {
	n--
	n |= n >> 1
	n |= n >> 2
	n |= n >> 4
	n |= n >> 8
	n |= n >> 16
	n |= n >> 32
	n++

	return n
}
<commit_msg>添加测试代码<commit_after>// Copyright (c) 2014 The SurgeMQ Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package service

import (
	"bufio"
	"fmt"
	"io"
	"sync/atomic"
	//"github.com/surgemq/message"
	"encoding/binary"
	"math"
	"sync"
)

var (
	bufcnt            int64
	DefaultBufferSize int64

	DeviceInBufferSize  int64
	DeviceOutBufferSize int64

	MasterInBufferSize  int64
	MasterOutBufferSize int64
)

const (
	smallReadBlockSize    = 512
	defaultReadBlockSize  = 8192
	defaultWriteBlockSize = 8192
)

/**
2016.03.03 修改
bingbuffer结构体
*/
type buffer struct {
	readIndex  int64        //读序号
	writeIndex int64        //写序号
	ringBuffer []*ByteArray //环形buffer指针数组
	bufferSize int64        //初始化环形buffer指针数组大小
	mask       int64        //掩码：bufferSize-1
	done       int64        //是否完成
	rcond      *sync.Cond
	wcond      *sync.Cond
}

type ByteArray struct {
	bArray []byte
}

func (this *buffer) ReadCommit(index int64) {
	this.rcond.L.Lock()
	defer this.rcond.L.Unlock()
	this.ringBuffer[index] = nil
	this.rcond.Broadcast()
	this.wcond.Broadcast()
}

func (this *buffer) Len() int {
	cpos := this.GetCurrentReadIndex()
	ppos := this.GetCurrentWriteIndex()
	return int(ppos - cpos)
}

/**
2016.03.03 添加
初始化ringbuffer
参数bufferSize：初始化环形buffer指针数组大小
*/
func newBuffer(size int64) (*buffer, error) {
	if size < 0 {
		return nil, bufio.ErrNegativeCount
	}
	if size == 0 {
		size = DefaultBufferSize
	}
	if !powerOfTwo64(size) {
		fmt.Printf("Size must be power of two. Try %d.", roundUpPowerOfTwo64(size))
		return nil, fmt.Errorf("Size must be power of two. Try %d.", roundUpPowerOfTwo64(size))
	}

	return &buffer{
		readIndex:  int64(0),                 //读序号
		writeIndex: int64(0),                 //写序号
		ringBuffer: make([]*ByteArray, size), //环形buffer指针数组
		bufferSize: size,                     //初始化环形buffer指针数组大小
		mask:       size - 1,
		rcond:      sync.NewCond(new(sync.Mutex)),
		wcond:      sync.NewCond(new(sync.Mutex)),
	}, nil
}

/**
2016.03.03 添加
获取当前读序号
*/
func (this *buffer) GetCurrentReadIndex() int64 {
	return atomic.LoadInt64(&this.readIndex)
}

/**
2016.03.03 添加
获取当前写序号
*/
func (this *buffer) GetCurrentWriteIndex() int64 {
	return atomic.LoadInt64(&this.writeIndex)
}

/**
2016.03.03 添加
读取ringbuffer指定的buffer指针，返回该指针并清空ringbuffer该位置存在的指针内容，以及将读序号加1
*/
func (this *buffer) ReadBuffer() ([]byte, int64, bool) {
	this.rcond.L.Lock()
	defer this.rcond.L.Unlock()

	for {
		readIndex := atomic.LoadInt64(&this.readIndex)
		writeIndex := atomic.LoadInt64(&this.writeIndex)
		switch {
		case readIndex >= writeIndex:
			this.rcond.Wait()
		case writeIndex-readIndex > this.bufferSize:
			this.rcond.Wait()
		default:
			if readIndex == math.MaxInt64 {
				atomic.StoreInt64(&this.readIndex, int64(0))
			} else {
				atomic.AddInt64(&this.readIndex, int64(1))
			}

			index := readIndex & this.mask

			p_ := this.ringBuffer[index]
			//this.ringBuffer[index] = nil
			if p_ == nil {
				return nil, -1, false
			}
			p := p_.bArray

			if p == nil {
				return nil, -1, false
			}

			return p, index, true
		}
	}

}

/**
2016.03.03 添加
写入ringbuffer指针，以及将写序号加1
*/
func (this *buffer) WriteBuffer(in []byte) bool {
	this.wcond.L.Lock()
	defer this.wcond.L.Unlock()

	for {
		readIndex := atomic.LoadInt64(&this.readIndex)
		writeIndex := atomic.LoadInt64(&this.writeIndex)
		switch {
		case writeIndex-readIndex < 0:
			this.wcond.Wait()
		default:
			index := writeIndex & this.mask
			if writeIndex == math.MaxInt64 {
				atomic.StoreInt64(&this.writeIndex, int64(0))
			} else {
				atomic.AddInt64(&this.writeIndex, int64(1))
			}
			if this.ringBuffer[index] == nil {
				this.ringBuffer[index] = &ByteArray{bArray: in}
				this.rcond.Broadcast()
				return true
			} else {
				this.rcond.Broadcast()
				return false
			}
		}
	}

}

/**
2016.03.03 修改
完成
*/
func (this *buffer) Close() error {
	//atomic.StoreInt64(&this.done, 1)

	this.wcond.L.Lock()
	this.rcond.Broadcast()
	this.wcond.L.Unlock()

	this.rcond.L.Lock()
	this.wcond.Broadcast()
	this.rcond.L.Unlock()

	return nil
}

/*

/**
2016.03.03 修改
向ringbuffer中写数据（从connection的中向ringbuffer中写）--生产者
*/
func (this *buffer) ReadFrom(r io.Reader) (int64, error) {
	defer this.Close()
	total := int64(0)
	//for {

	//if this.isDone() {
	//	return total, io.EOF
	//}
	b := make([]byte, int64(5))
	n, err := r.Read(b[0:1])

	if n > 0 {
		total += int64(n)
		if err != nil {
			return total, err
		}
	}

	/**************************/
	cnt := 1

	// Let's read enough bytes to get the message header (msg type, remaining length)
	for {
		// If we have read 5 bytes and still not done, then there's a problem.
		if cnt > 4 {
			return 0, fmt.Errorf("sendrecv/peekMessageSize: 4th byte of remaining length has continuation bit set")
		}

		Log.Infoc(func() string {
			return fmt.Sprintf("sendrecv/peekMessageSize: %d=========", cnt)
		})
		// Peek cnt bytes from the input buffer.

		_, err := r.Read(b[cnt:(cnt + 1)])
		//fmt.Println(b)
		if err != nil {
			return 0, err
		}
		// If we got enough bytes, then check the last byte to see if the continuation
		// bit is set. If so, increment cnt and continue peeking
		if b[cnt] >= 0x80 {
			cnt++
		} else {
			break
		}
	}

	// Get the remaining length of the message
	remlen, m := binary.Uvarint(b[1:])
	//Log.Infoc(func() string {
	//	return fmt.Sprintf("b[cnt:(cnt + 1)]==end")
	//})
	// Total message length is remlen + 1 (msg type) + m (remlen bytes)

	total = int64(remlen) + int64(1) + int64(m)
	//Log.Infoc(func() string {
	//	return fmt.Sprintf("remlen===n===totle: %d===%d===%d", remlen, m, total)
	//})
	//mtype := message.MessageType(b[0] >> 4)
	/****************/
	//var msg message.Message
	//
	//msg, err = mtype.New()
	//if err != nil {
	//	return 0, err
	//}
	b_ := make([]byte, int64(remlen))
	_, err = r.Read(b_[0:])
	if err != nil {
		fmt.Println("写入buffer失败，total：%d", total)
		return total, err
	}
	b__ := make([]byte, 0, total)
	fmt.Println(b_)
	b__ = append(b__, b[0:1+m]...)
	b__ = append(b__, b_[0:]...)
	fmt.Println(b__)
	//n, err = msg.Decode(b)
	//if err != nil {
	//	return 0, err
	//}

	/*************************/

	if !this.WriteBuffer(b__) {
		return total, err
	}

	return total, nil
	//}
}

/**
2016.03.03 修改
*/
func (this *buffer) WriteTo(w io.Writer) (int64, error) {
	defer this.Close()
	total := int64(0)
	//for {
	//if this.isDone() {
	//	return total, io.EOF
	//}
	p, index, ok := this.ReadBuffer()
	defer this.ReadCommit(index)
	if !ok {
		return total, io.EOF
	}

	Log.Debugc(func() string {
		return fmt.Sprintf("defer this.ReadCommit(%s)", index)
	})
	Log.Debugc(func() string {
		return fmt.Sprintf("WriteTo函数》》读取*p：" + string(p))
	})

	Log.Debugc(func() string {
		return fmt.Sprintf(" WriteTo(w io.Writer)(7)")
	})
	//
	//Log.Errorc(func() string {
	//	return fmt.Sprintf("msg::" + msg.Name())
	//})
	//
	//p := make([]byte, msg.Len())
	//_, err := msg.Encode(p)
	//if err != nil {
	//	Log.Errorc(func() string {
	//		return fmt.Sprintf("msg.Encode(p)")
	//	})
	//	return total, io.EOF
	//}
	// There's some data, let's process it first
	if len(p) > 0 {
		n, err := w.Write(p)
		total += int64(n)
		Log.Debugc(func() string {
			return fmt.Sprintf("Wrote %d bytes, totaling %d bytes", n, total)
		})

		if err != nil {
			Log.Errorc(func() string {
				return fmt.Sprintf("w.Write(p) error")
			})
			return total, err
		}
	}

	return total, nil
	//}
}

/**
2016.03.03 修改
*/
func (this *buffer) isDone() bool {
	//if atomic.LoadInt64(&this.done) == 1 {
	//	return true
	//}

	return false
}

func powerOfTwo64(n int64) bool {
	return n != 0 && (n&(n-1)) == 0
}

func roundUpPowerOfTwo64(n int64) int64 {
	n--
	n |= n >> 1
	n |= n >> 2
	n |= n >> 4
	n |= n >> 8
	n |= n >> 16
	n |= n >> 32
	n++

	return n
}
<|endoftext|>


<commit_before>// Copyright (c) 2014 The SurgeMQ Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package service

import (
	"bufio"
	"fmt"
	"io"
	"sync/atomic"
	//"github.com/surgemq/message"
	"encoding/binary"
	"math"
	"sync"
)

var (
	bufcnt            int64
	DefaultBufferSize int64

	DeviceInBufferSize  int64
	DeviceOutBufferSize int64

	MasterInBufferSize  int64
	MasterOutBufferSize int64
)

const (
	smallReadBlockSize    = 512
	defaultReadBlockSize  = 8192
	defaultWriteBlockSize = 8192
)

/**
2016.03.03 修改
bingbuffer结构体
*/
type buffer struct {
	readIndex  int64        //读序号
	writeIndex int64        //写序号
	ringBuffer []*ByteArray //环形buffer指针数组
	bufferSize int64        //初始化环形buffer指针数组大小
	mask       int64        //掩码：bufferSize-1
	done       int64        //是否完成
	rcond      *sync.Cond
	wcond      *sync.Cond
}

type ByteArray struct {
	bArray []byte
}

func (this *buffer) ReadCommit(index int64) {
	this.rcond.L.Lock()
	defer this.rcond.L.Unlock()
	this.ringBuffer[index] = nil
	this.rcond.Broadcast()
	this.wcond.Broadcast()
}

func (this *buffer) Len() int {
	cpos := this.GetCurrentReadIndex()
	ppos := this.GetCurrentWriteIndex()
	return int(ppos - cpos)
}

/**
2016.03.03 添加
初始化ringbuffer
参数bufferSize：初始化环形buffer指针数组大小
*/
func newBuffer(size int64) (*buffer, error) {
	if size < 0 {
		return nil, bufio.ErrNegativeCount
	}
	if size == 0 {
		size = DefaultBufferSize
	}
	if !powerOfTwo64(size) {
		fmt.Printf("Size must be power of two. Try %d.", roundUpPowerOfTwo64(size))
		return nil, fmt.Errorf("Size must be power of two. Try %d.", roundUpPowerOfTwo64(size))
	}

	return &buffer{
		readIndex:  int64(0),                 //读序号
		writeIndex: int64(0),                 //写序号
		ringBuffer: make([]*ByteArray, size), //环形buffer指针数组
		bufferSize: size,                     //初始化环形buffer指针数组大小
		mask:       size - 1,
		rcond:      sync.NewCond(new(sync.Mutex)),
		wcond:      sync.NewCond(new(sync.Mutex)),
	}, nil
}

/**
2016.03.03 添加
获取当前读序号
*/
func (this *buffer) GetCurrentReadIndex() int64 {
	return atomic.LoadInt64(&this.readIndex)
}

/**
2016.03.03 添加
获取当前写序号
*/
func (this *buffer) GetCurrentWriteIndex() int64 {
	return atomic.LoadInt64(&this.writeIndex)
}

/**
2016.03.03 添加
读取ringbuffer指定的buffer指针，返回该指针并清空ringbuffer该位置存在的指针内容，以及将读序号加1
*/
func (this *buffer) ReadBuffer() ([]byte, int64, bool) {
	this.rcond.L.Lock()
	defer this.rcond.L.Unlock()

	for {
		readIndex := atomic.LoadInt64(&this.readIndex)
		writeIndex := atomic.LoadInt64(&this.writeIndex)
		switch {
		case readIndex >= writeIndex:
			this.rcond.Wait()
		case writeIndex-readIndex > this.bufferSize:
			this.rcond.Wait()
		default:
			if readIndex == math.MaxInt64 {
				atomic.StoreInt64(&this.readIndex, int64(0))
			} else {
				atomic.AddInt64(&this.readIndex, int64(1))
			}

			index := readIndex & this.mask

			p_ := this.ringBuffer[index]
			//this.ringBuffer[index] = nil
			if p_ == nil {
				return nil, -1, false
			}
			p := p_.bArray

			if p == nil {
				return nil, -1, false
			}

			return p, index, true
		}
	}

}

/**
2016.03.03 添加
写入ringbuffer指针，以及将写序号加1
*/
func (this *buffer) WriteBuffer(in []byte) bool {
	this.wcond.L.Lock()
	defer this.wcond.L.Unlock()

	for {
		readIndex := atomic.LoadInt64(&this.readIndex)
		writeIndex := atomic.LoadInt64(&this.writeIndex)
		switch {
		case writeIndex-readIndex < 0:
			this.wcond.Wait()
		default:
			index := writeIndex & this.mask
			if writeIndex == math.MaxInt64 {
				atomic.StoreInt64(&this.writeIndex, int64(0))
			} else {
				atomic.AddInt64(&this.writeIndex, int64(1))
			}
			if this.ringBuffer[index] == nil {
				this.ringBuffer[index] = &ByteArray{bArray: in}
				this.rcond.Broadcast()
				return true
			} else {
				this.rcond.Broadcast()
				return false
			}
		}
	}

}

/**
2016.03.03 修改
完成
*/
func (this *buffer) Close() error {
	//atomic.StoreInt64(&this.done, 1)

	this.wcond.L.Lock()
	this.rcond.Broadcast()
	this.wcond.L.Unlock()

	this.rcond.L.Lock()
	this.wcond.Broadcast()
	this.rcond.L.Unlock()

	return nil
}

/*

/**
2016.03.03 修改
向ringbuffer中写数据（从connection的中向ringbuffer中写）--生产者
*/
func (this *buffer) ReadFrom(r io.Reader) (int64, error) {
	defer this.Close()
	total := int64(0)
	//for {

	//if this.isDone() {
	//	return total, io.EOF
	//}
	b := make([]byte, 5)
	n, err := r.Read(b[0:1])

	if n > 0 {
		total += int64(n)
		if err != nil {
			return total, err
		}
	}

	/**************************/
	cnt := 1

	// Let's read enough bytes to get the message header (msg type, remaining length)
	for {
		// If we have read 5 bytes and still not done, then there's a problem.
		if cnt > 4 {
			return 0, fmt.Errorf("sendrecv/peekMessageSize: 4th byte of remaining length has continuation bit set")
		}

		Log.Infoc(func() string {
			return fmt.Sprintf("sendrecv/peekMessageSize: %d=========", cnt)
		})
		// Peek cnt bytes from the input buffer.

		_, err := r.Read(b[cnt:(cnt + 1)])
		//fmt.Println(b)
		if err != nil {
			return 0, err
		}
		// If we got enough bytes, then check the last byte to see if the continuation
		// bit is set. If so, increment cnt and continue peeking
		if b[cnt] >= 0x80 {
			cnt++
		} else {
			break
		}
	}

	// Get the remaining length of the message
	remlen, m := binary.Uvarint(b[1:])
	//Log.Infoc(func() string {
	//	return fmt.Sprintf("b[cnt:(cnt + 1)]==end")
	//})
	// Total message length is remlen + 1 (msg type) + m (remlen bytes)

	total = remlen + int64(1) + int64(m)
	//Log.Infoc(func() string {
	//	return fmt.Sprintf("remlen===n===totle: %d===%d===%d", remlen, m, total)
	//})
	//mtype := message.MessageType(b[0] >> 4)
	/****************/
	//var msg message.Message
	//
	//msg, err = mtype.New()
	//if err != nil {
	//	return 0, err
	//}
	b_ := make([]byte,remlen)
	_, err = r.Read(b_[0:])
	if err != nil {
		fmt.Println("写入buffer失败，total：%d", total)
		return total, err
	}
	b = append(b, b_...)

	//n, err = msg.Decode(b)
	//if err != nil {
	//	return 0, err
	//}

	/*************************/

	if !this.WriteBuffer(b) {
		return total, err
	}

	return total, nil
	//}
}

/**
2016.03.03 修改
*/
func (this *buffer) WriteTo(w io.Writer) (int64, error) {
	defer this.Close()
	total := int64(0)
	//for {
	//if this.isDone() {
	//	return total, io.EOF
	//}
	p, index, ok := this.ReadBuffer()
	defer this.ReadCommit(index)
	if !ok {
		return total, io.EOF
	}

	Log.Debugc(func() string {
		return fmt.Sprintf("defer this.ReadCommit(%s)", index)
	})
	Log.Debugc(func() string {
		return fmt.Sprintf("WriteTo函数》》读取*p：" + string(p))
	})

	Log.Debugc(func() string {
		return fmt.Sprintf(" WriteTo(w io.Writer)(7)")
	})
	//
	//Log.Errorc(func() string {
	//	return fmt.Sprintf("msg::" + msg.Name())
	//})
	//
	//p := make([]byte, msg.Len())
	//_, err := msg.Encode(p)
	//if err != nil {
	//	Log.Errorc(func() string {
	//		return fmt.Sprintf("msg.Encode(p)")
	//	})
	//	return total, io.EOF
	//}
	// There's some data, let's process it first
	if len(p) > 0 {
		n, err := w.Write(p)
		total += int64(n)
		Log.Debugc(func() string {
			return fmt.Sprintf("Wrote %d bytes, totaling %d bytes", n, total)
		})

		if err != nil {
			Log.Errorc(func() string {
				return fmt.Sprintf("w.Write(p) error")
			})
			return total, err
		}
	}

	return total, nil
	//}
}

/**
2016.03.03 修改
*/
func (this *buffer) isDone() bool {
	//if atomic.LoadInt64(&this.done) == 1 {
	//	return true
	//}

	return false
}

func powerOfTwo64(n int64) bool {
	return n != 0 && (n&(n-1)) == 0
}

func roundUpPowerOfTwo64(n int64) int64 {
	n--
	n |= n >> 1
	n |= n >> 2
	n |= n >> 4
	n |= n >> 8
	n |= n >> 16
	n |= n >> 32
	n++

	return n
}
<commit_msg>添加测试代码<commit_after>// Copyright (c) 2014 The SurgeMQ Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package service

import (
	"bufio"
	"fmt"
	"io"
	"sync/atomic"
	//"github.com/surgemq/message"
	"encoding/binary"
	"math"
	"sync"
)

var (
	bufcnt            int64
	DefaultBufferSize int64

	DeviceInBufferSize  int64
	DeviceOutBufferSize int64

	MasterInBufferSize  int64
	MasterOutBufferSize int64
)

const (
	smallReadBlockSize    = 512
	defaultReadBlockSize  = 8192
	defaultWriteBlockSize = 8192
)

/**
2016.03.03 修改
bingbuffer结构体
*/
type buffer struct {
	readIndex  int64        //读序号
	writeIndex int64        //写序号
	ringBuffer []*ByteArray //环形buffer指针数组
	bufferSize int64        //初始化环形buffer指针数组大小
	mask       int64        //掩码：bufferSize-1
	done       int64        //是否完成
	rcond      *sync.Cond
	wcond      *sync.Cond
}

type ByteArray struct {
	bArray []byte
}

func (this *buffer) ReadCommit(index int64) {
	this.rcond.L.Lock()
	defer this.rcond.L.Unlock()
	this.ringBuffer[index] = nil
	this.rcond.Broadcast()
	this.wcond.Broadcast()
}

func (this *buffer) Len() int {
	cpos := this.GetCurrentReadIndex()
	ppos := this.GetCurrentWriteIndex()
	return int(ppos - cpos)
}

/**
2016.03.03 添加
初始化ringbuffer
参数bufferSize：初始化环形buffer指针数组大小
*/
func newBuffer(size int64) (*buffer, error) {
	if size < 0 {
		return nil, bufio.ErrNegativeCount
	}
	if size == 0 {
		size = DefaultBufferSize
	}
	if !powerOfTwo64(size) {
		fmt.Printf("Size must be power of two. Try %d.", roundUpPowerOfTwo64(size))
		return nil, fmt.Errorf("Size must be power of two. Try %d.", roundUpPowerOfTwo64(size))
	}

	return &buffer{
		readIndex:  int64(0),                 //读序号
		writeIndex: int64(0),                 //写序号
		ringBuffer: make([]*ByteArray, size), //环形buffer指针数组
		bufferSize: size,                     //初始化环形buffer指针数组大小
		mask:       size - 1,
		rcond:      sync.NewCond(new(sync.Mutex)),
		wcond:      sync.NewCond(new(sync.Mutex)),
	}, nil
}

/**
2016.03.03 添加
获取当前读序号
*/
func (this *buffer) GetCurrentReadIndex() int64 {
	return atomic.LoadInt64(&this.readIndex)
}

/**
2016.03.03 添加
获取当前写序号
*/
func (this *buffer) GetCurrentWriteIndex() int64 {
	return atomic.LoadInt64(&this.writeIndex)
}

/**
2016.03.03 添加
读取ringbuffer指定的buffer指针，返回该指针并清空ringbuffer该位置存在的指针内容，以及将读序号加1
*/
func (this *buffer) ReadBuffer() ([]byte, int64, bool) {
	this.rcond.L.Lock()
	defer this.rcond.L.Unlock()

	for {
		readIndex := atomic.LoadInt64(&this.readIndex)
		writeIndex := atomic.LoadInt64(&this.writeIndex)
		switch {
		case readIndex >= writeIndex:
			this.rcond.Wait()
		case writeIndex-readIndex > this.bufferSize:
			this.rcond.Wait()
		default:
			if readIndex == math.MaxInt64 {
				atomic.StoreInt64(&this.readIndex, int64(0))
			} else {
				atomic.AddInt64(&this.readIndex, int64(1))
			}

			index := readIndex & this.mask

			p_ := this.ringBuffer[index]
			//this.ringBuffer[index] = nil
			if p_ == nil {
				return nil, -1, false
			}
			p := p_.bArray

			if p == nil {
				return nil, -1, false
			}

			return p, index, true
		}
	}

}

/**
2016.03.03 添加
写入ringbuffer指针，以及将写序号加1
*/
func (this *buffer) WriteBuffer(in []byte) bool {
	this.wcond.L.Lock()
	defer this.wcond.L.Unlock()

	for {
		readIndex := atomic.LoadInt64(&this.readIndex)
		writeIndex := atomic.LoadInt64(&this.writeIndex)
		switch {
		case writeIndex-readIndex < 0:
			this.wcond.Wait()
		default:
			index := writeIndex & this.mask
			if writeIndex == math.MaxInt64 {
				atomic.StoreInt64(&this.writeIndex, int64(0))
			} else {
				atomic.AddInt64(&this.writeIndex, int64(1))
			}
			if this.ringBuffer[index] == nil {
				this.ringBuffer[index] = &ByteArray{bArray: in}
				this.rcond.Broadcast()
				return true
			} else {
				this.rcond.Broadcast()
				return false
			}
		}
	}

}

/**
2016.03.03 修改
完成
*/
func (this *buffer) Close() error {
	//atomic.StoreInt64(&this.done, 1)

	this.wcond.L.Lock()
	this.rcond.Broadcast()
	this.wcond.L.Unlock()

	this.rcond.L.Lock()
	this.wcond.Broadcast()
	this.rcond.L.Unlock()

	return nil
}

/*

/**
2016.03.03 修改
向ringbuffer中写数据（从connection的中向ringbuffer中写）--生产者
*/
func (this *buffer) ReadFrom(r io.Reader) (int64, error) {
	defer this.Close()
	total := int64(0)
	//for {

	//if this.isDone() {
	//	return total, io.EOF
	//}
	b := make([]byte, int64(5))
	n, err := r.Read(b[0:1])

	if n > 0 {
		total += int64(n)
		if err != nil {
			return total, err
		}
	}

	/**************************/
	cnt := 1

	// Let's read enough bytes to get the message header (msg type, remaining length)
	for {
		// If we have read 5 bytes and still not done, then there's a problem.
		if cnt > 4 {
			return 0, fmt.Errorf("sendrecv/peekMessageSize: 4th byte of remaining length has continuation bit set")
		}

		Log.Infoc(func() string {
			return fmt.Sprintf("sendrecv/peekMessageSize: %d=========", cnt)
		})
		// Peek cnt bytes from the input buffer.

		_, err := r.Read(b[cnt:(cnt + 1)])
		//fmt.Println(b)
		if err != nil {
			return 0, err
		}
		// If we got enough bytes, then check the last byte to see if the continuation
		// bit is set. If so, increment cnt and continue peeking
		if b[cnt] >= 0x80 {
			cnt++
		} else {
			break
		}
	}

	// Get the remaining length of the message
	remlen, m := binary.Uvarint(b[1:])
	//Log.Infoc(func() string {
	//	return fmt.Sprintf("b[cnt:(cnt + 1)]==end")
	//})
	// Total message length is remlen + 1 (msg type) + m (remlen bytes)

	total = int64(remlen) + int64(1) + int64(m)
	//Log.Infoc(func() string {
	//	return fmt.Sprintf("remlen===n===totle: %d===%d===%d", remlen, m, total)
	//})
	//mtype := message.MessageType(b[0] >> 4)
	/****************/
	//var msg message.Message
	//
	//msg, err = mtype.New()
	//if err != nil {
	//	return 0, err
	//}
	b_ := make([]byte, int64(remlen))
	_, err = r.Read(b_[0:])
	if err != nil {
		fmt.Println("写入buffer失败，total：%d", total)
		return total, err
	}
	b = append(b, b_...)

	//n, err = msg.Decode(b)
	//if err != nil {
	//	return 0, err
	//}

	/*************************/

	if !this.WriteBuffer(b) {
		return total, err
	}

	return total, nil
	//}
}

/**
2016.03.03 修改
*/
func (this *buffer) WriteTo(w io.Writer) (int64, error) {
	defer this.Close()
	total := int64(0)
	//for {
	//if this.isDone() {
	//	return total, io.EOF
	//}
	p, index, ok := this.ReadBuffer()
	defer this.ReadCommit(index)
	if !ok {
		return total, io.EOF
	}

	Log.Debugc(func() string {
		return fmt.Sprintf("defer this.ReadCommit(%s)", index)
	})
	Log.Debugc(func() string {
		return fmt.Sprintf("WriteTo函数》》读取*p：" + string(p))
	})

	Log.Debugc(func() string {
		return fmt.Sprintf(" WriteTo(w io.Writer)(7)")
	})
	//
	//Log.Errorc(func() string {
	//	return fmt.Sprintf("msg::" + msg.Name())
	//})
	//
	//p := make([]byte, msg.Len())
	//_, err := msg.Encode(p)
	//if err != nil {
	//	Log.Errorc(func() string {
	//		return fmt.Sprintf("msg.Encode(p)")
	//	})
	//	return total, io.EOF
	//}
	// There's some data, let's process it first
	if len(p) > 0 {
		n, err := w.Write(p)
		total += int64(n)
		Log.Debugc(func() string {
			return fmt.Sprintf("Wrote %d bytes, totaling %d bytes", n, total)
		})

		if err != nil {
			Log.Errorc(func() string {
				return fmt.Sprintf("w.Write(p) error")
			})
			return total, err
		}
	}

	return total, nil
	//}
}

/**
2016.03.03 修改
*/
func (this *buffer) isDone() bool {
	//if atomic.LoadInt64(&this.done) == 1 {
	//	return true
	//}

	return false
}

func powerOfTwo64(n int64) bool {
	return n != 0 && (n&(n-1)) == 0
}

func roundUpPowerOfTwo64(n int64) int64 {
	n--
	n |= n >> 1
	n |= n >> 2
	n |= n >> 4
	n |= n >> 8
	n |= n >> 16
	n |= n >> 32
	n++

	return n
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"math/big"
	"strings"

	"github.com/spf13/cobra"

	"github.com/NebulousLabs/Sia/api"
	"github.com/NebulousLabs/Sia/modules"
)

// coinUnits converts a siacoin amount to base units.
func coinUnits(amount string) (string, error) {
	units := []string{"pS", "nS", "uS", "mS", "SC", "KS", "MS", "GS", "TS"}
	for i, unit := range units {
		if strings.HasSuffix(amount, unit) {
			// scan into big.Rat
			r, ok := new(big.Rat).SetString(strings.TrimSuffix(amount, unit))
			if !ok {
				return "", errors.New("malformed amount")
			}
			// convert units
			exp := 24 + 3*(int64(i)-4)
			mag := new(big.Int).Exp(big.NewInt(10), big.NewInt(exp), nil)
			r.Mul(r, new(big.Rat).SetInt(mag))
			// r must be an integer at this point
			if !r.IsInt() {
				return "", errors.New("non-integer number of hastings")
			}
			return r.RatString(), nil
		}
	}
	return amount, nil // hastings
}

var (
	walletCmd = &cobra.Command{
		Use:   "wallet",
		Short: "Perform wallet actions",
		Long: `Generate a new address, send coins to another wallet, or view info about the wallet.
Units:
The smallest unit of siacoins is the hasting. One siacoin is 10^24 hastings. Other supported units are:
	pS (pico,  10^-12 SC)
	nS (nano,  10^-9 SC)
	uS (micro, 10^-6 SC)
	mS (milli, 10^-3 SC)
	SC
	KS (kilo, 10^3 SC)
	MS (mega, 10^6 SC)
	GS (giga, 10^9 SC)
	TS (tera, 10^12 SC)`,
		Run: wrap(walletstatuscmd),
	}

	walletAddressCmd = &cobra.Command{
		Use:   "address",
		Short: "Get a new wallet address",
		Long:  "Generate a new wallet address.",
		Run:   wrap(walletaddresscmd),
	}

	walletSendCmd = &cobra.Command{
		Use:   "send [amount] [dest]",
		Short: "Send coins to another wallet",
		Long: `Send coins to another wallet. 'dest' must be a 76-byte hexadecimal address.
'amount' can be specified in units, e.g. 1.23KS. Run 'wallet --help' for a list of units.
If no unit is supplied, hastings will be assumed.`,
		Run: wrap(walletsendcmd),
	}

	walletSiafundsCmd = &cobra.Command{
		Use:   "siafunds",
		Short: "Display siafunds balance",
		Long:  "Display siafunds balance and siacoin claim balance.",
		Run:   wrap(walletsiafundscmd),
	}

	walletSiafundsSendCmd = &cobra.Command{
		Use:   "send [amount] [dest] [keyfiles]",
		Short: "Send siafunds",
		Long: `Send siafunds to an address, and transfer their siacoins to the wallet.
Run 'wallet send --help' to see a list of available units.`,
		Run: walletsiafundssendcmd, // see function docstring
	}

	walletSiafundsTrackCmd = &cobra.Command{
		Use:   "track [keyfile]",
		Short: "Track a siafund address generated by siag",
		Long:  "Track a siafund address generated by siag.",
		Run:   wrap(walletsiafundstrackcmd),
	}

	walletStatusCmd = &cobra.Command{
		Use:   "status",
		Short: "View wallet status",
		Long:  "View wallet status, including the current balance and number of addresses.",
		Run:   wrap(walletstatuscmd),
	}
)

// TODO: this should be defined outside of siac
type walletAddr struct {
	Address string
}

func walletaddresscmd() {
	addr := new(walletAddr)
	err := getAPI("/wallet/address", addr)
	if err != nil {
		fmt.Println("Could not generate new address:", err)
		return
	}
	fmt.Printf("Created new address: %s\n", addr.Address)
}

func walletsendcmd(amount, dest string) {
	adjAmount, err := coinUnits(amount)
	if err != nil {
		fmt.Println("Could not parse amount:", err)
		return
	}
	err = post("/wallet/send", fmt.Sprintf("amount=%s&destination=%s", adjAmount, dest))
	if err != nil {
		fmt.Println("Could not send:", err)
		return
	}
	fmt.Printf("Sent %s to %s\n", adjAmount, dest)
}

func walletsiafundscmd() {
	bal := new(api.WalletSiafundsBalance)
	err := getAPI("/wallet/siafunds/balance", bal)
	if err != nil {
		fmt.Println("Could not get siafunds balance:", err)
		return
	}
	fmt.Printf("Siafunds Balance: %s\nClaim Balance: %s\n", bal.SiafundBalance, bal.SiacoinClaimBalance)
}

// special because list of keyfiles is variadic
func walletsiafundssendcmd(cmd *cobra.Command, args []string) {
	if len(args) < 3 {
		cmd.Usage()
		return
	}
	amount, dest, keyfiles := args[0], args[1], args[2:]
	for i := range keyfiles {
		keyfiles[i] = abs(keyfiles[i])
	}
	qs := fmt.Sprintf("amount=%s&destination=%s&keyfiles=%s", amount, dest, strings.Join(keyfiles, ","))

	err := post("/wallet/siafunds/send", qs)
	if err != nil {
		fmt.Println("Could not track siafunds:", err)
		return
	}
	fmt.Printf("Sent %s siafunds to %s\n", amount, dest)
}

func walletsiafundstrackcmd(keyfile string) {
	err := post("/wallet/siafunds/watchsiagaddress", "keyfile="+abs(keyfile))
	if err != nil {
		fmt.Println("Could not track siafunds:", err)
		return
	}
	fmt.Printf(`Added %s to tracked siafunds.

You must restart siad to update your siafund balance.
Do not delete the original keyfile.
`, keyfile)
}

func walletstatuscmd() {
	status := new(modules.WalletInfo)
	err := getAPI("/wallet/status", status)
	if err != nil {
		fmt.Println("Could not get wallet status:", err)
		return
	}
	// divide by 1e24 to get SC
	r := new(big.Rat).SetFrac(status.Balance.Big(), new(big.Int).Exp(big.NewInt(10), big.NewInt(24), nil))
	sc, _ := r.Float64()
	fmt.Printf(`Wallet status:
Balance:   %.2f SC
Exact:     %v
Addresses: %d
`, sc, status.Balance, status.NumAddresses)
}
<commit_msg>mention miner fee<commit_after>package main

import (
	"errors"
	"fmt"
	"math/big"
	"strings"

	"github.com/spf13/cobra"

	"github.com/NebulousLabs/Sia/api"
	"github.com/NebulousLabs/Sia/modules"
)

// coinUnits converts a siacoin amount to base units.
func coinUnits(amount string) (string, error) {
	units := []string{"pS", "nS", "uS", "mS", "SC", "KS", "MS", "GS", "TS"}
	for i, unit := range units {
		if strings.HasSuffix(amount, unit) {
			// scan into big.Rat
			r, ok := new(big.Rat).SetString(strings.TrimSuffix(amount, unit))
			if !ok {
				return "", errors.New("malformed amount")
			}
			// convert units
			exp := 24 + 3*(int64(i)-4)
			mag := new(big.Int).Exp(big.NewInt(10), big.NewInt(exp), nil)
			r.Mul(r, new(big.Rat).SetInt(mag))
			// r must be an integer at this point
			if !r.IsInt() {
				return "", errors.New("non-integer number of hastings")
			}
			return r.RatString(), nil
		}
	}
	return amount, nil // hastings
}

var (
	walletCmd = &cobra.Command{
		Use:   "wallet",
		Short: "Perform wallet actions",
		Long: `Generate a new address, send coins to another wallet, or view info about the wallet.

Units:
The smallest unit of siacoins is the hasting. One siacoin is 10^24 hastings. Other supported units are:
  pS (pico,  10^-12 SC)
  nS (nano,  10^-9 SC)
  uS (micro, 10^-6 SC)
  mS (milli, 10^-3 SC)
  SC
  KS (kilo, 10^3 SC)
  MS (mega, 10^6 SC)
  GS (giga, 10^9 SC)
  TS (tera, 10^12 SC)`,
		Run: wrap(walletstatuscmd),
	}

	walletAddressCmd = &cobra.Command{
		Use:   "address",
		Short: "Get a new wallet address",
		Long:  "Generate a new wallet address.",
		Run:   wrap(walletaddresscmd),
	}

	walletSendCmd = &cobra.Command{
		Use:   "send [amount] [dest]",
		Short: "Send coins to another wallet",
		Long: `Send coins to another wallet. 'dest' must be a 76-byte hexadecimal address.
'amount' can be specified in units, e.g. 1.23KS. Run 'wallet --help' for a list of units.
If no unit is supplied, hastings will be assumed.

A miner fee of 10 SC is levied on all transactions.`,
		Run: wrap(walletsendcmd),
	}

	walletSiafundsCmd = &cobra.Command{
		Use:   "siafunds",
		Short: "Display siafunds balance",
		Long:  "Display siafunds balance and siacoin claim balance.",
		Run:   wrap(walletsiafundscmd),
	}

	walletSiafundsSendCmd = &cobra.Command{
		Use:   "send [amount] [dest] [keyfiles]",
		Short: "Send siafunds",
		Long: `Send siafunds to an address, and transfer their siacoins to the wallet.
Run 'wallet send --help' to see a list of available units.`,
		Run: walletsiafundssendcmd, // see function docstring
	}

	walletSiafundsTrackCmd = &cobra.Command{
		Use:   "track [keyfile]",
		Short: "Track a siafund address generated by siag",
		Long:  "Track a siafund address generated by siag.",
		Run:   wrap(walletsiafundstrackcmd),
	}

	walletStatusCmd = &cobra.Command{
		Use:   "status",
		Short: "View wallet status",
		Long:  "View wallet status, including the current balance and number of addresses.",
		Run:   wrap(walletstatuscmd),
	}
)

// TODO: this should be defined outside of siac
type walletAddr struct {
	Address string
}

func walletaddresscmd() {
	addr := new(walletAddr)
	err := getAPI("/wallet/address", addr)
	if err != nil {
		fmt.Println("Could not generate new address:", err)
		return
	}
	fmt.Printf("Created new address: %s\n", addr.Address)
}

func walletsendcmd(amount, dest string) {
	adjAmount, err := coinUnits(amount)
	if err != nil {
		fmt.Println("Could not parse amount:", err)
		return
	}
	err = post("/wallet/send", fmt.Sprintf("amount=%s&destination=%s", adjAmount, dest))
	if err != nil {
		fmt.Println("Could not send:", err)
		return
	}
	fmt.Printf("Sent %s to %s\n", adjAmount, dest)
}

func walletsiafundscmd() {
	bal := new(api.WalletSiafundsBalance)
	err := getAPI("/wallet/siafunds/balance", bal)
	if err != nil {
		fmt.Println("Could not get siafunds balance:", err)
		return
	}
	fmt.Printf("Siafunds Balance: %s\nClaim Balance: %s\n", bal.SiafundBalance, bal.SiacoinClaimBalance)
}

// special because list of keyfiles is variadic
func walletsiafundssendcmd(cmd *cobra.Command, args []string) {
	if len(args) < 3 {
		cmd.Usage()
		return
	}
	amount, dest, keyfiles := args[0], args[1], args[2:]
	for i := range keyfiles {
		keyfiles[i] = abs(keyfiles[i])
	}
	qs := fmt.Sprintf("amount=%s&destination=%s&keyfiles=%s", amount, dest, strings.Join(keyfiles, ","))

	err := post("/wallet/siafunds/send", qs)
	if err != nil {
		fmt.Println("Could not track siafunds:", err)
		return
	}
	fmt.Printf("Sent %s siafunds to %s\n", amount, dest)
}

func walletsiafundstrackcmd(keyfile string) {
	err := post("/wallet/siafunds/watchsiagaddress", "keyfile="+abs(keyfile))
	if err != nil {
		fmt.Println("Could not track siafunds:", err)
		return
	}
	fmt.Printf(`Added %s to tracked siafunds.

You must restart siad to update your siafund balance.
Do not delete the original keyfile.
`, keyfile)
}

func walletstatuscmd() {
	status := new(modules.WalletInfo)
	err := getAPI("/wallet/status", status)
	if err != nil {
		fmt.Println("Could not get wallet status:", err)
		return
	}
	// divide by 1e24 to get SC
	r := new(big.Rat).SetFrac(status.Balance.Big(), new(big.Int).Exp(big.NewInt(10), big.NewInt(24), nil))
	sc, _ := r.Float64()
	fmt.Printf(`Wallet status:
Balance:   %.2f SC
Exact:     %v
Addresses: %d
`, sc, status.Balance, status.NumAddresses)
}
<|endoftext|>