package main

import (
	"fmt"
	"runtime"
	"strconv"
	"strings"
)

const jumlahPotongan = 500

var sink string

// buatPotongan menyiapkan data uji tetap di luar jendela pengukuran.
func buatPotongan() []string {
	kata := make([]string, 0, jumlahPotongan)
	for i := 0; i < jumlahPotongan; i++ {
		kata = append(kata, "potongan-"+strconv.Itoa(i))
	}
	return kata
}

// gabungPlus memakai operator + di dalam loop: setiap penyambungan
// mengalokasikan string baru dan menyalin seluruh isi lama.
func gabungPlus(potongan []string) string {
	hasil := ""
	for _, p := range potongan {
		hasil += p
	}
	return hasil
}

// gabungBuilder memakai strings.Builder: data ditampung di buffer
// internal yang tumbuh, string final dibuat sekali di akhir.
func gabungBuilder(potongan []string) string {
	var b strings.Builder
	for _, p := range potongan {
		b.WriteString(p)
	}
	return b.String()
}

// gabungBuilderGrow sama seperti di atas, tetapi kapasitas dihitung
// di muka sehingga buffer hanya dialokasikan satu kali.
func gabungBuilderGrow(potongan []string) string {
	total := 0
	for _, p := range potongan {
		total += len(p)
	}
	var b strings.Builder
	b.Grow(total)
	for _, p := range potongan {
		b.WriteString(p)
	}
	return b.String()
}

func main() {
	potongan := buatPotongan()
	var sebelum, sesudah runtime.MemStats

	ukur := func(f func() string) (alokasi int64, ukuran int64) {
		runtime.ReadMemStats(&sebelum)
		hasil := f()
		runtime.ReadMemStats(&sesudah)
		sink = hasil
		return int64(sesudah.Mallocs - sebelum.Mallocs),
			int64(sesudah.TotalAlloc - sebelum.TotalAlloc)
	}

	alokPlus, bytePlus := ukur(func() string { return gabungPlus(potongan) })
	alokBuilder, byteBuilder := ukur(func() string { return gabungBuilder(potongan) })
	alokGrow, byteGrow := ukur(func() string { return gabungBuilderGrow(potongan) })
	alokJoin, byteJoin := ukur(func() string { return strings.Join(potongan, "") })

	fmt.Println("Panjang hasil:", len(gabungPlus(potongan)), "karakter")
	fmt.Printf("operator +          : %d alokasi, %d byte\n", alokPlus, bytePlus)
	fmt.Printf("Builder tanpa Grow  : %d alokasi, %d byte\n", alokBuilder, byteBuilder)
	fmt.Printf("Builder dengan Grow : %d alokasi, %d byte\n", alokGrow, byteGrow)
	fmt.Printf("strings.Join        : %d alokasi, %d byte\n", alokJoin, byteJoin)
}
