Skip to content

Commit 36b421b

Browse files
committed
perf: optimize string concatenation using strings.Builder
- Replace string += concatenation with strings.Builder for better performance - Update RenderHeaderRow, RenderRow, formatCell, and RenderBorderLine methods - Improve memory efficiency in string building operations - Reduce allocations in hot paths during table rendering
1 parent 2dfc705 commit 36b421b

File tree

1 file changed

+47
-34
lines changed

1 file changed

+47
-34
lines changed

table.go

Lines changed: 47 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ func (t *Table) RenderHeader() error {
242242

243243
// RenderHeaderRow renders a header row with full-line styling
244244
func (t *Table) RenderHeaderRow(row Row) error {
245-
var line string
245+
var builder strings.Builder
246246
var stylePrefix, styleSuffix string
247247

248248
// Apply header style to the entire line if configured
@@ -252,11 +252,11 @@ func (t *Table) RenderHeaderRow(row Row) error {
252252
}
253253

254254
// Start the line with style prefix
255-
line = stylePrefix
255+
builder.WriteString(stylePrefix)
256256

257257
// Left border (only if enabled)
258258
if t.borderConfig.Left {
259-
line += t.borders["vertical"]
259+
builder.WriteString(t.borders["vertical"])
260260
}
261261

262262
for i, col := range t.columns {
@@ -279,40 +279,45 @@ func (t *Table) RenderHeaderRow(row Row) error {
279279
// No padding for empty cells
280280
content = strings.Repeat(" ", col.Width)
281281
} else {
282-
// Empty cell with padding
282+
// Empty cell with padding using strings.Builder
283+
var cellBuilder strings.Builder
283284
paddingStr := strings.Repeat(" ", t.padding)
284-
content = paddingStr + strings.Repeat(" ", col.Width) + paddingStr
285+
cellBuilder.WriteString(paddingStr)
286+
cellBuilder.WriteString(strings.Repeat(" ", col.Width))
287+
cellBuilder.WriteString(paddingStr)
288+
content = cellBuilder.String()
285289
}
286290
}
287291
}
288292

289-
line += content
293+
builder.WriteString(content)
290294

291295
// Add vertical separator between columns (only if enabled and not the last column)
292296
if t.borderConfig.Vertical && i < len(t.columns)-1 {
293-
line += t.borders["vertical"]
297+
builder.WriteString(t.borders["vertical"])
294298
}
295299
}
296300

297301
// Right border (only if enabled)
298302
if t.borderConfig.Right {
299-
line += t.borders["vertical"]
303+
builder.WriteString(t.borders["vertical"])
300304
}
301305

302306
// End the line with style suffix
303-
line += styleSuffix + "\n"
307+
builder.WriteString(styleSuffix)
308+
builder.WriteString("\n")
304309

305-
_, err := t.writer.Write([]byte(line))
310+
_, err := t.writer.Write([]byte(builder.String()))
306311
return err
307312
}
308313

309314
// RenderRow renders a single row
310315
func (t *Table) RenderRow(row Row) error {
311-
var line string
316+
var builder strings.Builder
312317

313318
// Left border (only if enabled)
314319
if t.borderConfig.Left {
315-
line = t.borders["vertical"]
320+
builder.WriteString(t.borders["vertical"])
316321
}
317322

318323
for i, col := range t.columns {
@@ -335,28 +340,32 @@ func (t *Table) RenderRow(row Row) error {
335340
// No padding for empty cells
336341
content = strings.Repeat(" ", col.Width)
337342
} else {
338-
// Empty cell with padding
343+
// Empty cell with padding using strings.Builder
344+
var cellBuilder strings.Builder
339345
paddingStr := strings.Repeat(" ", t.padding)
340-
content = paddingStr + strings.Repeat(" ", col.Width) + paddingStr
346+
cellBuilder.WriteString(paddingStr)
347+
cellBuilder.WriteString(strings.Repeat(" ", col.Width))
348+
cellBuilder.WriteString(paddingStr)
349+
content = cellBuilder.String()
341350
}
342351
}
343352
}
344353

345-
line += content
354+
builder.WriteString(content)
346355

347356
// Add vertical separator between columns (only if enabled and not the last column)
348357
if t.borderConfig.Vertical && i < len(t.columns)-1 {
349-
line += t.borders["vertical"]
358+
builder.WriteString(t.borders["vertical"])
350359
}
351360
}
352361

353362
// Right border (only if enabled)
354363
if t.borderConfig.Right {
355-
line += t.borders["vertical"]
364+
builder.WriteString(t.borders["vertical"])
356365
}
357366

358-
line += "\n"
359-
_, err := t.writer.Write([]byte(line))
367+
builder.WriteString("\n")
368+
_, err := t.writer.Write([]byte(builder.String()))
360369
return err
361370
}
362371

@@ -381,22 +390,26 @@ func (t *Table) formatCell(content string, width int, align string) string {
381390
// Apply alignment to the content width
382391
paddedContent := padString(content, width, align)
383392

384-
// Add padding spaces on both sides
393+
// Add padding spaces on both sides using strings.Builder for efficiency
394+
var builder strings.Builder
385395
paddingStr := strings.Repeat(" ", t.padding)
386-
return paddingStr + paddedContent + paddingStr
396+
builder.WriteString(paddingStr)
397+
builder.WriteString(paddedContent)
398+
builder.WriteString(paddingStr)
399+
return builder.String()
387400
}
388401

389402
// RenderBorderLine renders horizontal border lines
390403
func (t *Table) RenderBorderLine(position string) error {
391-
var line string
404+
var builder strings.Builder
392405

393406
switch position {
394407
case "top":
395-
line = t.borders["top_left"]
408+
builder.WriteString(t.borders["top_left"])
396409
case "bottom":
397-
line = t.borders["bottom_left"]
410+
builder.WriteString(t.borders["bottom_left"])
398411
default: // middle
399-
line = t.borders["left_cross"]
412+
builder.WriteString(t.borders["left_cross"])
400413
}
401414

402415
for i, col := range t.columns {
@@ -405,31 +418,31 @@ func (t *Table) RenderBorderLine(position string) error {
405418
if t.borderConfig.Padding {
406419
cellWidth += (t.padding * 2)
407420
}
408-
line += strings.Repeat(t.borders["horizontal"], cellWidth)
421+
builder.WriteString(strings.Repeat(t.borders["horizontal"], cellWidth))
409422

410423
if i < len(t.columns)-1 {
411424
switch position {
412425
case "top":
413-
line += t.borders["top_cross"]
426+
builder.WriteString(t.borders["top_cross"])
414427
case "bottom":
415-
line += t.borders["bottom_cross"]
428+
builder.WriteString(t.borders["bottom_cross"])
416429
default:
417-
line += t.borders["cross"]
430+
builder.WriteString(t.borders["cross"])
418431
}
419432
}
420433
}
421434

422435
switch position {
423436
case "top":
424-
line += t.borders["top_right"]
437+
builder.WriteString(t.borders["top_right"])
425438
case "bottom":
426-
line += t.borders["bottom_right"]
439+
builder.WriteString(t.borders["bottom_right"])
427440
default:
428-
line += t.borders["right_cross"]
441+
builder.WriteString(t.borders["right_cross"])
429442
}
430443

431-
line += "\n"
432-
_, err := t.writer.Write([]byte(line))
444+
builder.WriteString("\n")
445+
_, err := t.writer.Write([]byte(builder.String()))
433446
return err
434447
}
435448

0 commit comments

Comments
 (0)