func Sub(s string, start, end int) string {
	return sub(s, start, end)
}
Sub returns substring from specified string avoiding panics with index start and end.
start, end are based on unicode(utf8) count.
package stringx
import (
	"errors"
	"math"
	"strings"
	"unicode/utf8"
	"github.com/bytedance/gopkg/internal/hack"
	"github.com/bytedance/gopkg/lang/fastrand"
)
var (
	ErrDecodeRune = errors.New("error occurred on rune decoding")
)
func PadLeftChar(s string, size int, ch rune) string {
	return padCharLeftOrRight(s, size, ch, true)
}
func PadLeftSpace(s string, size int) string {
	return PadLeftChar(s, size, ' ')
}
func PadRightChar(s string, size int, ch rune) string {
	return padCharLeftOrRight(s, size, ch, false)
}
func PadRightSpace(s string, size int) string {
	return PadRightChar(s, size, ' ')
}
func PadCenterChar(s string, size int, ch rune) string {
	if size <= 0 {
		return s
	}
	length := utf8.RuneCountInString(s)
	pads := size - length
	if pads <= 0 {
		return s
	}
	leftPads := pads / 2
	if leftPads > 0 {
		s = padRawLeftChar(s, ch, leftPads)
	}
	rightPads := size - leftPads - length
	if rightPads > 0 {
		s = padRawRightChar(s, ch, rightPads)
	}
	return s
}
func PadCenterSpace(s string, size int) string {
	return PadCenterChar(s, size, ' ')
}
func padCharLeftOrRight(s string, size int, ch rune, isLeft bool) string {
	if size <= 0 {
		return s
	}
	pads := size - utf8.RuneCountInString(s)
	if pads <= 0 {
		return s
	}
	if isLeft {
		return padRawLeftChar(s, ch, pads)
	}
	return padRawRightChar(s, ch, pads)
}
func padRawLeftChar(s string, ch rune, padSize int) string {
	return RepeatChar(ch, padSize) + s
}
func padRawRightChar(s string, ch rune, padSize int) string {
	return s + RepeatChar(ch, padSize)
}
func RepeatChar(ch rune, repeat int) string {
	if repeat <= 0 {
		return ""
	}
	sb := strings.Builder{}
	sb.Grow(repeat)
	for i := 0; i < repeat; i++ {
		sb.WriteRune(ch)
	}
	return sb.String()
}
func RemoveChar(s string, rmVal rune) string {
	if s == "" {
		return s
	}
	sb := strings.Builder{}
	sb.Grow(len(s) / 2)
	for _, v := range s {
		if v != rmVal {
			sb.WriteRune(v)
		}
	}
	return sb.String()
}
func RemoveString(s, rmStr string) string {
	if s == "" || rmStr == "" {
		return s
	}
	return strings.ReplaceAll(s, rmStr, "")
}
func Rotate(s string, shift int) string {
	if shift == 0 {
		return s
	}
	sLen := len(s)
	if sLen == 0 {
		return s
	}
	shiftMod := shift % sLen
	if shiftMod == 0 {
		return s
	}
	offset := -(shiftMod)
	sb := strings.Builder{}
	sb.Grow(sLen)
	_, _ = sb.WriteString(SubStart(s, offset))
	_, _ = sb.WriteString(Sub(s, 0, offset))
	return sb.String()
}