Home > Mobile >  why *(*string)(unsafe.Pointer(&b)) doesn't work with bufio.Reader
why *(*string)(unsafe.Pointer(&b)) doesn't work with bufio.Reader

Time:12-13

i have a file. it has some ip

1.1.1.0/24
1.1.2.0/24
2.2.1.0/24
2.2.2.0/24

i read this file to slice, and used *(*string)(unsafe.Pointer(&b)) to parse []byte to string, but is doesn't work

func TestInitIpRangeFromFile(t *testing.T) {
   filepath := "/tmp/test"
   file, err := os.Open(filepath)
   if err != nil {
      t.Errorf("failed to open ip range file:%s, err:%s", filepath, err)
   }
   reader := bufio.NewReader(file)
   ranges := make([]string, 0)
   for {
      ip, _, err := reader.ReadLine()
      if err != nil {
         if err == io.EOF {
            break
         }
         logger.Fatalf("failed to read ip range file, err:%s", err)
      }
      t.Logf("ip:%s", *(*string)(unsafe.Pointer(&ip)))
      ranges = append(ranges, *(*string)(unsafe.Pointer(&ip)))
   }

   t.Logf("%v", ranges)
}

result:

    task_test.go:71: ip:1.1.1.0/24
    task_test.go:71: ip:1.1.2.0/24
    task_test.go:71: ip:2.2.1.0/24
    task_test.go:71: ip:2.2.2.0/24
    task_test.go:75: [2.2.2.0/24 1.1.2.0/24 2.2.1.0/24 2.2.2.0/24]

why 1.1.1.0/24 changed to 2.2.2.0/24 ?

change

*(*string)(unsafe.Pointer(&ip))

to string(ip) it works

CodePudding user response:

So, while reinterpreting a slice-header as a string-header the way you did is absolutely bonkers and has no guarantee whatsoever of working correctly, it's only indirectly the cause of your problem.

The real problem is that you're retaining a pointer to the return value of bufio/Reader.ReadLine(), but the docs for that method say "The returned buffer is only valid until the next call to ReadLine." Which means that the reader is free to reuse that memory later on, and that's what's happening.

When you do the cast in the proper way, string(ip), Go copies the contents of the buffer into the newly-created string, which remains valid in the future. But when you type-pun the slice into a string, you keep the exact same pointer, which stops working as soon as the reader refills its buffer.

If you decided to do the pointer trickery as a performance hack to avoid copying and allocation... too bad. The reader interface is going to force you to copy the data out anyway, and since it does, you should just use string().

  •  Tags:  
  • go
  • Related